@myspec/mcp-server 0.2.0-next.80 → 0.2.0-next.82
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 +38 -1
- package/dist/index.js +121 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,6 +64,10 @@ value, including the dot** and paste it back into the terminal. Two traps:
|
|
|
64
64
|
without a person present (a server, a container, a CI job, a shared machine),
|
|
65
65
|
use an API token instead.
|
|
66
66
|
|
|
67
|
+
> Needs **`@myspec/mcp-server` 0.2.0 or newer** (`npm view @myspec/mcp-server
|
|
68
|
+
> version`). Earlier builds ignore the `apiToken` field and report that they are
|
|
69
|
+
> not signed in, without mentioning the version.
|
|
70
|
+
|
|
67
71
|
**1. Create the token.** In the MySpec webapp, open the avatar menu → **API
|
|
68
72
|
tokens** → **Create token**. Choose the organization it should act in, whether
|
|
69
73
|
it may write or only read, and an expiry. The token is shown **once**; it
|
|
@@ -233,13 +237,46 @@ Local state is stored under `~/.myspec/` (alongside the on-disk cache root), spl
|
|
|
233
237
|
| `get_project` | `project_id` | Project + active sessions + files + attachments |
|
|
234
238
|
| `get_spec_file` | `file_id`, optional `include_download_url` | File metadata, including `content_version` (see [Concurrent edits](#concurrent-edits-content_version)); with `include_download_url=N` also returns a download URL targeting revision `N` |
|
|
235
239
|
| `read_spec_file` | `file_id`, optional `revision` | UTF-8 content of the file, plus `content_version` when reading the latest revision (errors for binary or files > 1 MiB — use `download_spec_file` for those). Cached on disk under `MYSPEC_DOWNLOAD_ROOT` |
|
|
236
|
-
| `download_spec_file` | `file_id`, `destination_path`, optional `revision`, optional `overwrite` | Downloads the file's raw bytes to
|
|
240
|
+
| `download_spec_file` | `file_id`, optional `destination_path`, optional `revision`, optional `overwrite` | Downloads the file's raw bytes to a local file — by default under `.specs/` in the working directory (see [Download destinations](#download-destinations)). Handles binary files and files up to 50 MiB. Refuses to replace an existing file unless `overwrite=true`. Returns only metadata (`saved_path`, `bytes_written`, `revision_number`, `destination_source`) — **not** the content, so a large file can be saved locally and read in chunks |
|
|
237
241
|
| `list_spec_file` | `project_id`, optional `path`, optional `trashed` | Lists a project's spec files (`file_id`, `file_path`, `file_type`, `file_size_bytes`, `revision_count`, `session_id`, `updated_at`). Filter to a directory with `path` (exact file or everything under it; `""`/`"/"`/omit = all). `trashed=true` lists the Trash Bin instead of live files |
|
|
238
242
|
| `move_spec_file_to_trash` | `file_id` | Moves a spec file to the Trash Bin (recoverable — hidden from listings and AI-agent tools, restorable indefinitely). Not a permanent delete |
|
|
239
243
|
| `restore_spec_file_from_trash` | `file_id` | Restores a trashed spec file back to the active file list, reversing `move_spec_file_to_trash` |
|
|
240
244
|
| `get_attachment` | `attachment_id`, optional `include_download_url` | Attachment metadata (looked up org-scoped by id); with `include_download_url>0` also returns a signed download URL |
|
|
241
245
|
| `upload_attachment` | `project_id`, `file_path` (absolute), optional `file_name`, optional `mime_type`, optional `override` | Uploads the local file as a new attachment on the project. Returns `attachment_id`, `file_name` (the **effective** name the platform stored — may differ from the requested one when auto-dedup adds a `(N)` suffix), `has_file_name_conflict` (`true` when stored name ≠ requested name), `mime_type`, `file_size_bytes`, `checksum` (format: `sha256:<hex>`), `file_uri`, `reused_existing_attachment` (`true` when an existing attachment with the same name AND same checksum was found and returned without re-uploading; `file_uri` is then omitted because the list endpoint does not surface it — call `get_attachment` for a download URL). With `override=true`, any existing attachment whose recorded name matches is soft-deleted first (returned as `overridden_attachment_ids` — empty array means nothing matched) and the new upload keeps the requested name; the same-checksum short-circuit still applies, so a no-op re-upload never deletes anything. Max 10 MB. Supported content: PDF, DOCX, XLSX, and any UTF-8 text file (XML, JSON, YAML, HTML, Markdown, CSV, source code, ...) |
|
|
242
246
|
|
|
247
|
+
### Download destinations
|
|
248
|
+
|
|
249
|
+
`download_spec_file` prefers the **`.specs/` folder in the MCP server's working
|
|
250
|
+
directory** — dot-prefixed because it is conventionally gitignored, so a
|
|
251
|
+
downloaded copy never shows up as untracked noise beside a repo's own `specs/`.
|
|
252
|
+
|
|
253
|
+
- **`destination_path` omitted** → the file's remote path is mirrored under
|
|
254
|
+
`.specs/`, with the remote `specs/` root replaced:
|
|
255
|
+
`specs/add-oauth/proposal.md` → `.specs/add-oauth/proposal.md`. Any other root
|
|
256
|
+
is kept beneath `.specs/` (`openspec/changes/add-x/spec.md` →
|
|
257
|
+
`.specs/openspec/changes/add-x/spec.md`), so every download lands in one folder.
|
|
258
|
+
That includes the legacy `.spec/` prefix on old files, which nests as
|
|
259
|
+
`.specs/.spec/…` — the platform has required `specs/` or `openspec/` since,
|
|
260
|
+
so only pre-existing files land there.
|
|
261
|
+
- **Relative `destination_path` rooted at `specs/`** → redirected to `.specs/`.
|
|
262
|
+
This is the remote path echoed back, and it would otherwise write into the
|
|
263
|
+
repo's own spec folder.
|
|
264
|
+
- **Anything else** → honored exactly as given. An absolute path is the escape
|
|
265
|
+
hatch for saving into a literal `specs/` directory.
|
|
266
|
+
|
|
267
|
+
The response reports which of the three applied as `destination_source`
|
|
268
|
+
(`default` / `redirected` / `explicit`), plus a `note` for the first two, so a
|
|
269
|
+
caller that passed `specs/...` and got `.specs/...` can see why.
|
|
270
|
+
|
|
271
|
+
A `destination_path` that names a directory — it exists as one, or ends with a
|
|
272
|
+
path separator — gets the spec file's basename appended. Across a redirect both
|
|
273
|
+
the path as passed and the rewritten one are consulted, so whichever exists as a
|
|
274
|
+
directory wins: `specs/add-oauth/`, an existing `specs/add-oauth/`, and an
|
|
275
|
+
existing `.specs/add-oauth/` all land at `.specs/add-oauth/<basename>` rather
|
|
276
|
+
than at a file named `.specs/add-oauth`. A bare `specs` is always the folder.
|
|
277
|
+
Derived paths (default and redirected) are confined to `.specs/` and may not be
|
|
278
|
+
`.specs/` itself; an explicit `destination_path` is not sandboxed.
|
|
279
|
+
|
|
243
280
|
### `include_download_url` semantics
|
|
244
281
|
|
|
245
282
|
`get_spec_file`:
|
package/dist/index.js
CHANGED
|
@@ -565,12 +565,20 @@ var TokenManager = class {
|
|
|
565
565
|
this.apiTokenRejection = rejection;
|
|
566
566
|
throw rejection;
|
|
567
567
|
}
|
|
568
|
+
if (response.status === 404) {
|
|
569
|
+
const body = await response.text().catch(() => "");
|
|
570
|
+
throw new HttpStatusError(
|
|
571
|
+
response.status,
|
|
572
|
+
body,
|
|
573
|
+
`Token exchange failed: ${url} returned 404, so no exchange endpoint is there. This URL comes from userAuthUrl in ~/.myspec/settings.json \u2014 check it names the environment the token was created in, and rewrite it with \`npx @myspec/mcp-server login --user-auth-url <url>\`. Note that --user-auth-url and MYSPEC_USER_AUTH_URL do not redirect the exchange on their own, and userAuthUrl in oauth_creds.json is ignored entirely.`
|
|
574
|
+
);
|
|
575
|
+
}
|
|
568
576
|
if (!response.ok) {
|
|
569
577
|
const body = await response.text().catch(() => "");
|
|
570
578
|
throw new HttpStatusError(
|
|
571
579
|
response.status,
|
|
572
580
|
body,
|
|
573
|
-
`Token exchange failed: HTTP ${String(response.status)}`
|
|
581
|
+
`Token exchange failed: HTTP ${String(response.status)} from ${url}`
|
|
574
582
|
);
|
|
575
583
|
}
|
|
576
584
|
const payload = await response.json().catch(() => null);
|
|
@@ -2624,11 +2632,37 @@ function resolveDownloadRoot(env = process.env) {
|
|
|
2624
2632
|
}
|
|
2625
2633
|
return path2.join(os2.homedir(), ".myspec");
|
|
2626
2634
|
}
|
|
2635
|
+
var LOCAL_SPEC_DIR = ".specs";
|
|
2636
|
+
var REMOTE_SPEC_ROOT = "specs";
|
|
2637
|
+
function defaultSpecDestination(remoteFilePath) {
|
|
2638
|
+
const segments = remoteFilePath.split("/").filter((s) => s !== "" && s !== ".");
|
|
2639
|
+
if (segments[0] === REMOTE_SPEC_ROOT) {
|
|
2640
|
+
segments.shift();
|
|
2641
|
+
}
|
|
2642
|
+
if (segments.length === 0) {
|
|
2643
|
+
segments.push("content");
|
|
2644
|
+
}
|
|
2645
|
+
return path2.join(LOCAL_SPEC_DIR, ...segments);
|
|
2646
|
+
}
|
|
2647
|
+
function redirectSpecRootToLocal(destPath) {
|
|
2648
|
+
if (path2.isAbsolute(destPath)) {
|
|
2649
|
+
return null;
|
|
2650
|
+
}
|
|
2651
|
+
const segments = path2.normalize(destPath).split(path2.sep);
|
|
2652
|
+
if (segments[0] !== REMOTE_SPEC_ROOT) {
|
|
2653
|
+
return null;
|
|
2654
|
+
}
|
|
2655
|
+
if (segments.length === 1) {
|
|
2656
|
+
return LOCAL_SPEC_DIR + path2.sep;
|
|
2657
|
+
}
|
|
2658
|
+
segments[0] = LOCAL_SPEC_DIR;
|
|
2659
|
+
return segments.join(path2.sep);
|
|
2660
|
+
}
|
|
2627
2661
|
function assertWithinRoot(target, root) {
|
|
2628
2662
|
const resolvedTarget = path2.resolve(target);
|
|
2629
2663
|
const resolvedRoot = path2.resolve(root);
|
|
2630
2664
|
const rel = path2.relative(resolvedRoot, resolvedTarget);
|
|
2631
|
-
if (rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
2665
|
+
if (rel === ".." || rel.startsWith(".." + path2.sep) || path2.isAbsolute(rel)) {
|
|
2632
2666
|
return `path resolves outside the configured root (${resolvedRoot}): ${resolvedTarget}`;
|
|
2633
2667
|
}
|
|
2634
2668
|
return null;
|
|
@@ -2659,20 +2693,25 @@ function maskHomedir(p, home = os2.homedir()) {
|
|
|
2659
2693
|
return p;
|
|
2660
2694
|
}
|
|
2661
2695
|
async function resolveDestinationFile(destPath, basename4) {
|
|
2662
|
-
const absPath =
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2696
|
+
const absPath = toAbsoluteDestination(destPath);
|
|
2697
|
+
return await isDirectoryDestination(destPath) ? path2.join(absPath, basename4) : absPath;
|
|
2698
|
+
}
|
|
2699
|
+
async function isDirectoryDestination(destPath) {
|
|
2700
|
+
if (destPath.endsWith(path2.sep) || destPath.endsWith("/")) {
|
|
2701
|
+
return true;
|
|
2702
|
+
}
|
|
2703
|
+
try {
|
|
2704
|
+
const info = await fs2.stat(toAbsoluteDestination(destPath));
|
|
2705
|
+
return info.isDirectory();
|
|
2706
|
+
} catch (err) {
|
|
2707
|
+
if (!isFsNotFound2(err)) {
|
|
2708
|
+
throw err;
|
|
2673
2709
|
}
|
|
2710
|
+
return false;
|
|
2674
2711
|
}
|
|
2675
|
-
|
|
2712
|
+
}
|
|
2713
|
+
function toAbsoluteDestination(destPath) {
|
|
2714
|
+
return path2.isAbsolute(destPath) ? path2.resolve(destPath) : path2.resolve(process.cwd(), destPath);
|
|
2676
2715
|
}
|
|
2677
2716
|
async function pathExists(p) {
|
|
2678
2717
|
try {
|
|
@@ -2713,14 +2752,14 @@ var MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024;
|
|
|
2713
2752
|
var inputSchema4 = {
|
|
2714
2753
|
file_id: z4.string().min(1).describe("UUID of the file"),
|
|
2715
2754
|
revision: z4.number().int().positive().optional().describe("Specific revision number to download (defaults to the latest revision)."),
|
|
2716
|
-
destination_path: z4.string().min(1).describe(
|
|
2717
|
-
"Local path to save the file to.
|
|
2755
|
+
destination_path: z4.string().min(1).optional().describe(
|
|
2756
|
+
"Local path to save the file to. Optional: omit it to save the file under `.specs/` in the MCP server's working directory, mirroring its remote path (`specs/add-oauth/proposal.md` \u2192 `.specs/add-oauth/proposal.md`). When given, it is absolute or relative to that working directory; a relative path rooted at `specs/` is redirected to `.specs/`, so pass an absolute path to save into a literal `specs/` directory. If it points to an existing directory (or ends with a path separator), the spec file's basename is appended."
|
|
2718
2757
|
),
|
|
2719
2758
|
overwrite: z4.boolean().optional().describe("Overwrite the destination file if it already exists (default: false).")
|
|
2720
2759
|
};
|
|
2721
2760
|
var downloadSpecFileTool = {
|
|
2722
2761
|
name: "download_spec_file",
|
|
2723
|
-
description: "Download a spec file's raw bytes to a local file
|
|
2762
|
+
description: "Download a spec file's raw bytes to a local file. By default it lands under `.specs/` in the MCP server's working directory, mirroring the file's remote path \u2014 pass `destination_path` only to save it somewhere else. Unlike read_spec_file, this handles binary files and files up to 50 MiB, and the response returns only metadata (saved path, size, revision) \u2014 not the content. Use it to save a large or binary file locally, then search or read it in chunks with other tools.",
|
|
2724
2763
|
inputSchema: inputSchema4,
|
|
2725
2764
|
handler: async (args, ctx) => {
|
|
2726
2765
|
const meta = await ctx.client.getFileMetadata(args.file_id);
|
|
@@ -2729,12 +2768,35 @@ var downloadSpecFileTool = {
|
|
|
2729
2768
|
return errorResult(buildOversizeMessage(args.file_id, meta.fileSizeBytes));
|
|
2730
2769
|
}
|
|
2731
2770
|
const basename4 = path3.basename(meta.filePath) || "content";
|
|
2771
|
+
const requested = args.destination_path;
|
|
2772
|
+
let redirected = null;
|
|
2732
2773
|
let destFile;
|
|
2774
|
+
let destination = requested ?? "";
|
|
2733
2775
|
try {
|
|
2734
|
-
|
|
2776
|
+
if (requested === void 0) {
|
|
2777
|
+
destination = defaultSpecDestination(meta.filePath);
|
|
2778
|
+
} else {
|
|
2779
|
+
redirected = await carryDirectoryIntent(requested, redirectSpecRootToLocal(requested));
|
|
2780
|
+
destination = redirected ?? requested;
|
|
2781
|
+
}
|
|
2782
|
+
destFile = await resolveDestinationFile(destination, basename4);
|
|
2735
2783
|
} catch (err) {
|
|
2736
2784
|
const message = err instanceof Error ? err.message : String(err);
|
|
2737
|
-
return errorResult(`Failed to resolve destination_path ${
|
|
2785
|
+
return errorResult(`Failed to resolve destination_path ${maskHomedir(destination)}: ${message}`);
|
|
2786
|
+
}
|
|
2787
|
+
if (requested === void 0 || redirected !== null) {
|
|
2788
|
+
const specRoot = path3.resolve(process.cwd(), LOCAL_SPEC_DIR);
|
|
2789
|
+
const hint = requested === void 0 ? "Pass a destination_path to save elsewhere." : "Pass an absolute path to save into a literal specs/ directory.";
|
|
2790
|
+
if (assertWithinRoot(destFile, specRoot) !== null) {
|
|
2791
|
+
return errorResult(
|
|
2792
|
+
`download_spec_file refused to write outside ${LOCAL_SPEC_DIR}/ (${maskHomedir(specRoot)}): ${maskHomedir(destFile)}. ${hint}`
|
|
2793
|
+
);
|
|
2794
|
+
}
|
|
2795
|
+
if (path3.resolve(destFile) === specRoot) {
|
|
2796
|
+
return errorResult(
|
|
2797
|
+
`download_spec_file refused to write ${maskHomedir(destFile)}: that is the ${LOCAL_SPEC_DIR}/ folder itself, not a file inside it. ${hint}`
|
|
2798
|
+
);
|
|
2799
|
+
}
|
|
2738
2800
|
}
|
|
2739
2801
|
if (!args.overwrite && await pathExists(destFile)) {
|
|
2740
2802
|
return errorResult(
|
|
@@ -2752,16 +2814,45 @@ var downloadSpecFileTool = {
|
|
|
2752
2814
|
const message = err instanceof Error ? err.message : String(err);
|
|
2753
2815
|
return errorResult(`Failed to write ${maskHomedir(destFile)}: ${message}`);
|
|
2754
2816
|
}
|
|
2817
|
+
const source = requested === void 0 ? "default" : redirected !== null ? "redirected" : "explicit";
|
|
2755
2818
|
return jsonResult({
|
|
2756
2819
|
file_id: meta.id,
|
|
2757
2820
|
revision_number: downloaded.revisionNumber,
|
|
2758
2821
|
file_path: meta.filePath,
|
|
2759
2822
|
file_type: meta.fileType,
|
|
2760
2823
|
saved_path: maskHomedir(destFile),
|
|
2761
|
-
bytes_written: bytes.byteLength
|
|
2824
|
+
bytes_written: bytes.byteLength,
|
|
2825
|
+
// Say where the path came from, so a caller that passed `specs/...` and got
|
|
2826
|
+
// `.specs/...` back can see why rather than hunting for a missing file.
|
|
2827
|
+
destination_source: source,
|
|
2828
|
+
...source === "explicit" ? {} : { note: buildDestinationNote(source, requested) }
|
|
2762
2829
|
});
|
|
2763
2830
|
}
|
|
2764
2831
|
};
|
|
2832
|
+
async function carryDirectoryIntent(requested, rewritten) {
|
|
2833
|
+
if (rewritten === null || rewritten.endsWith(path3.sep)) {
|
|
2834
|
+
return rewritten;
|
|
2835
|
+
}
|
|
2836
|
+
let isDir;
|
|
2837
|
+
try {
|
|
2838
|
+
isDir = await isDirectoryDestination(requested);
|
|
2839
|
+
} catch (err) {
|
|
2840
|
+
if (!isFilesystemError(err)) {
|
|
2841
|
+
throw err;
|
|
2842
|
+
}
|
|
2843
|
+
isDir = false;
|
|
2844
|
+
}
|
|
2845
|
+
return isDir ? rewritten + path3.sep : rewritten;
|
|
2846
|
+
}
|
|
2847
|
+
function isFilesystemError(err) {
|
|
2848
|
+
return typeof err === "object" && err !== null && typeof err.code === "string";
|
|
2849
|
+
}
|
|
2850
|
+
function buildDestinationNote(source, requested) {
|
|
2851
|
+
if (source === "default") {
|
|
2852
|
+
return `destination_path was omitted, so the file was saved under the preferred ${LOCAL_SPEC_DIR}/ folder in the working directory.`;
|
|
2853
|
+
}
|
|
2854
|
+
return `destination_path ${String(requested)} was rooted at specs/ and was redirected to the preferred ${LOCAL_SPEC_DIR}/ folder. Pass an absolute path to save into a literal specs/ directory.`;
|
|
2855
|
+
}
|
|
2765
2856
|
function buildOversizeMessage(fileId, byteLength) {
|
|
2766
2857
|
return `File ${fileId} is ${String(byteLength)} bytes which exceeds the download cap of ${String(MAX_DOWNLOAD_BYTES)} bytes.`;
|
|
2767
2858
|
}
|
|
@@ -3041,7 +3132,7 @@ var inputSchema11 = {
|
|
|
3041
3132
|
};
|
|
3042
3133
|
var readSpecFileTool = {
|
|
3043
3134
|
name: "read_spec_file",
|
|
3044
|
-
description: "Return a spec file's UTF-8 content. Bytes are cached on disk under the configured cache root (default ~/.myspec) at `project/<projectId>/file/<fileId>/rev/<rev>/<basename>` and reused on subsequent reads of the same revision. Fails for binary files or files larger than 1 MiB \u2014 use download_spec_file
|
|
3135
|
+
description: "Return a spec file's UTF-8 content. Bytes are cached on disk under the configured cache root (default ~/.myspec) at `project/<projectId>/file/<fileId>/rev/<rev>/<basename>` and reused on subsequent reads of the same revision. Fails for binary files or files larger than 1 MiB \u2014 use download_spec_file to save those to a local file instead.",
|
|
3045
3136
|
inputSchema: inputSchema11,
|
|
3046
3137
|
handler: async (args, ctx) => {
|
|
3047
3138
|
const meta = await ctx.client.getFileMetadata(args.file_id);
|
|
@@ -3082,7 +3173,7 @@ var readSpecFileTool = {
|
|
|
3082
3173
|
}
|
|
3083
3174
|
if (isBinary(bytes)) {
|
|
3084
3175
|
return errorResult(
|
|
3085
|
-
`File ${args.file_id} appears to be binary (file_type=${meta.fileType}, size=${String(bytes.byteLength)} bytes). Use download_spec_file
|
|
3176
|
+
`File ${args.file_id} appears to be binary (file_type=${meta.fileType}, size=${String(bytes.byteLength)} bytes). Use download_spec_file to save it to a local file.`
|
|
3086
3177
|
);
|
|
3087
3178
|
}
|
|
3088
3179
|
let text;
|
|
@@ -3090,7 +3181,7 @@ var readSpecFileTool = {
|
|
|
3090
3181
|
text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
3091
3182
|
} catch {
|
|
3092
3183
|
return errorResult(
|
|
3093
|
-
`File ${args.file_id} is not valid UTF-8 text. Use download_spec_file
|
|
3184
|
+
`File ${args.file_id} is not valid UTF-8 text. Use download_spec_file to save it to a local file.`
|
|
3094
3185
|
);
|
|
3095
3186
|
}
|
|
3096
3187
|
return jsonResult({
|
|
@@ -3111,7 +3202,7 @@ var readSpecFileTool = {
|
|
|
3111
3202
|
}
|
|
3112
3203
|
};
|
|
3113
3204
|
function buildOversizeMessage2(fileId, byteLength) {
|
|
3114
|
-
return `File ${fileId} is ${String(byteLength)} bytes which exceeds the read cap of ${String(MAX_READ_BYTES)} bytes. Use download_spec_file
|
|
3205
|
+
return `File ${fileId} is ${String(byteLength)} bytes which exceeds the read cap of ${String(MAX_READ_BYTES)} bytes. Use download_spec_file to save it to a local file and read it in chunks.`;
|
|
3115
3206
|
}
|
|
3116
3207
|
function isBinary(bytes) {
|
|
3117
3208
|
for (const byte of bytes) {
|
|
@@ -5057,13 +5148,16 @@ function printHelp() {
|
|
|
5057
5148
|
" login --org <slug> Sign in and pin that organization. Tokens only work",
|
|
5058
5149
|
" when an organization is active; the CLI asks when you",
|
|
5059
5150
|
" belong to several and this flag is not given.",
|
|
5060
|
-
" logout
|
|
5151
|
+
" logout Sign out and clear the session credentials. A",
|
|
5152
|
+
" configured apiToken is KEPT, so an unattended",
|
|
5153
|
+
" install keeps working.",
|
|
5061
5154
|
" reverse --root <dir> Connect to ai-agent and expose local_fs tools.",
|
|
5062
5155
|
" The agent WebSocket URL is discovered from the webapp",
|
|
5063
5156
|
" (GET /api/v1/config) using your saved credentials;",
|
|
5064
5157
|
" override with --agent-url <url> or MYSPEC_AI_AGENT_WS_URL.",
|
|
5065
|
-
" Uses the
|
|
5066
|
-
"
|
|
5158
|
+
" Uses the apiToken from ~/.myspec/oauth_creds.json if one",
|
|
5159
|
+
" is set, otherwise the refresh_token saved by `login`, to",
|
|
5160
|
+
" obtain & auto-refresh access tokens. Override with",
|
|
5067
5161
|
" --access-token <jwt> or MYSPEC_ACCESS_TOKEN for local testing.",
|
|
5068
5162
|
" --version Print version",
|
|
5069
5163
|
" --help Print this help",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myspec/mcp-server",
|
|
3
|
-
"version": "0.2.0-next.
|
|
3
|
+
"version": "0.2.0-next.82",
|
|
4
4
|
"description": "MySpec MCP server — exposes MySpec platform projects, files and attachments to MCP-aware clients via OAuth-authenticated access tokens.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|