@myspec/mcp-server 0.1.2-next.37 → 0.1.2-next.38
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/dist/index.js +118 -42
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -99,7 +99,7 @@ function applyCorsHeaders(res) {
|
|
|
99
99
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
100
100
|
}
|
|
101
101
|
async function waitForOAuthCallback(opts) {
|
|
102
|
-
return new Promise((
|
|
102
|
+
return new Promise((resolve3, reject) => {
|
|
103
103
|
const settledFlag = { value: false };
|
|
104
104
|
const timeoutRef = { handle: void 0 };
|
|
105
105
|
const rlRef = { handle: void 0 };
|
|
@@ -157,7 +157,7 @@ async function waitForOAuthCallback(opts) {
|
|
|
157
157
|
res.setHeader("Content-Type", "text/html");
|
|
158
158
|
res.end(SUCCESS_HTML);
|
|
159
159
|
settle(() => {
|
|
160
|
-
|
|
160
|
+
resolve3({ code });
|
|
161
161
|
});
|
|
162
162
|
});
|
|
163
163
|
server.on("error", (err) => {
|
|
@@ -204,7 +204,7 @@ async function waitForOAuthCallback(opts) {
|
|
|
204
204
|
}
|
|
205
205
|
settle(() => {
|
|
206
206
|
opts.logger("Captured pasted token; finishing sign-in...");
|
|
207
|
-
|
|
207
|
+
resolve3({ code });
|
|
208
208
|
});
|
|
209
209
|
});
|
|
210
210
|
}
|
|
@@ -348,10 +348,10 @@ function createFileCredentialsStore(paths = {}) {
|
|
|
348
348
|
async function loadOauthCreds(target) {
|
|
349
349
|
try {
|
|
350
350
|
if (process.platform !== "win32") {
|
|
351
|
-
const
|
|
352
|
-
if ((
|
|
351
|
+
const stat2 = await fs.stat(target);
|
|
352
|
+
if ((stat2.mode & 63) !== 0) {
|
|
353
353
|
throw new Error(
|
|
354
|
-
`Refusing to load credentials from ${target}: file mode is too permissive (${(
|
|
354
|
+
`Refusing to load credentials from ${target}: file mode is too permissive (${(stat2.mode & 511).toString(8)}). Run \`chmod 600 ${target}\` and retry.`
|
|
355
355
|
);
|
|
356
356
|
}
|
|
357
357
|
}
|
|
@@ -840,7 +840,7 @@ function calculateDelay(attempt, config) {
|
|
|
840
840
|
return Math.round(cappedDelay * jitterMultiplier);
|
|
841
841
|
}
|
|
842
842
|
function sleep(ms) {
|
|
843
|
-
return new Promise((
|
|
843
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
844
844
|
}
|
|
845
845
|
async function withRetry(fn, config = DEFAULT_RETRY_CONFIG, shouldRetry = defaultShouldRetry) {
|
|
846
846
|
const startTime = Date.now();
|
|
@@ -2480,6 +2480,59 @@ var updateProjectTool = {
|
|
|
2480
2480
|
// src/server/tools/update_spec_file.ts
|
|
2481
2481
|
import { z as z11 } from "zod";
|
|
2482
2482
|
|
|
2483
|
+
// src/server/tools/spec-content-source.ts
|
|
2484
|
+
import { readFile, stat } from "fs/promises";
|
|
2485
|
+
import { isAbsolute, resolve } from "path";
|
|
2486
|
+
var MAX_LOCAL_SPEC_FILE_BYTES = 5 * 1024 * 1024;
|
|
2487
|
+
async function resolveSpecContent(input, maxBytes = MAX_LOCAL_SPEC_FILE_BYTES) {
|
|
2488
|
+
const { content, localFilePath } = input;
|
|
2489
|
+
if (content !== void 0 && localFilePath !== void 0) {
|
|
2490
|
+
return { error: "Provide either content or local_file_path, not both." };
|
|
2491
|
+
}
|
|
2492
|
+
if (content !== void 0) {
|
|
2493
|
+
return { bytes: new TextEncoder().encode(content) };
|
|
2494
|
+
}
|
|
2495
|
+
if (localFilePath === void 0) {
|
|
2496
|
+
return { error: "Provide the file body via content or local_file_path (one is required)." };
|
|
2497
|
+
}
|
|
2498
|
+
const absPath = isAbsolute(localFilePath) ? localFilePath : resolve(process.cwd(), localFilePath);
|
|
2499
|
+
let info;
|
|
2500
|
+
try {
|
|
2501
|
+
info = await stat(absPath);
|
|
2502
|
+
} catch (err) {
|
|
2503
|
+
const code = err.code;
|
|
2504
|
+
if (code === "ENOENT") {
|
|
2505
|
+
return { error: `local file not found: ${absPath}` };
|
|
2506
|
+
}
|
|
2507
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2508
|
+
return { error: `failed to read local file ${absPath}: ${message}` };
|
|
2509
|
+
}
|
|
2510
|
+
if (!info.isFile()) {
|
|
2511
|
+
return { error: `local_file_path is not a regular file: ${absPath}` };
|
|
2512
|
+
}
|
|
2513
|
+
if (info.size === 0) {
|
|
2514
|
+
return { error: `local file is empty: ${absPath}` };
|
|
2515
|
+
}
|
|
2516
|
+
if (info.size > maxBytes) {
|
|
2517
|
+
return {
|
|
2518
|
+
error: `local file is too large (${String(info.size)} bytes); max is ${String(maxBytes)} bytes.`
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
let raw;
|
|
2522
|
+
try {
|
|
2523
|
+
raw = await readFile(absPath);
|
|
2524
|
+
} catch (err) {
|
|
2525
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2526
|
+
return { error: `failed to read local file ${absPath}: ${message}` };
|
|
2527
|
+
}
|
|
2528
|
+
if (raw.byteLength > maxBytes) {
|
|
2529
|
+
return {
|
|
2530
|
+
error: `local file is too large (${String(raw.byteLength)} bytes); max is ${String(maxBytes)} bytes.`
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
return { bytes: new Uint8Array(raw) };
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2483
2536
|
// src/server/tools/spec-file-output.ts
|
|
2484
2537
|
function specFileSummary(file) {
|
|
2485
2538
|
return {
|
|
@@ -2501,13 +2554,25 @@ var inputSchema11 = {
|
|
|
2501
2554
|
file_id: z11.string().min(1).optional().describe("UUID of the spec file to overwrite. Provide this or (project_id + file_path)."),
|
|
2502
2555
|
project_id: z11.string().min(1).optional().describe("UUID of the project. Required (with file_path) when file_id is not given."),
|
|
2503
2556
|
file_path: z11.string().min(1).optional().describe("Path of the spec file. Required (with project_id) when file_id is not given."),
|
|
2504
|
-
content: z11.string().min(1).
|
|
2557
|
+
content: z11.string().min(1).optional().describe(
|
|
2558
|
+
"Full UTF-8 replacement content, saved as a new revision. Provide this OR local_file_path (exactly one). Use for in-memory / generated content."
|
|
2559
|
+
),
|
|
2560
|
+
local_file_path: z11.string().min(1).optional().describe(
|
|
2561
|
+
"Path to a local file whose bytes become the new revision; the server reads it directly. Provide this OR content (exactly one). Absolute paths are most reliable; a relative path resolves against the MCP server working directory. Point this only at an intended spec file \u2014 its bytes are uploaded to the project as-is; never use it for secrets or unrelated files."
|
|
2562
|
+
)
|
|
2505
2563
|
};
|
|
2506
2564
|
var updateSpecFileTool = {
|
|
2507
2565
|
name: "update_spec_file",
|
|
2508
|
-
description: "Overwrite an existing spec file by saving a new revision
|
|
2566
|
+
description: "Overwrite an existing spec file by saving a new revision. Provide the new body inline via `content` or by pointing at a local file with `local_file_path` \u2014 exactly one is required. Prefer `local_file_path` when the body is already an existing local file (the server reads it directly); use `content` for text generated in-memory. Reference the file by file_id or by (project_id + file_path).",
|
|
2509
2567
|
inputSchema: inputSchema11,
|
|
2510
2568
|
handler: async (args, ctx) => {
|
|
2569
|
+
const resolved = await resolveSpecContent({
|
|
2570
|
+
content: args.content,
|
|
2571
|
+
localFilePath: args.local_file_path
|
|
2572
|
+
});
|
|
2573
|
+
if (resolved.error || !resolved.bytes) {
|
|
2574
|
+
return errorResult(resolved.error ?? "Failed to resolve file content.");
|
|
2575
|
+
}
|
|
2511
2576
|
let fileId;
|
|
2512
2577
|
if (args.file_id) {
|
|
2513
2578
|
const meta = await ctx.client.getFileMetadata(args.file_id).catch(() => null);
|
|
@@ -2526,16 +2591,15 @@ var updateSpecFileTool = {
|
|
|
2526
2591
|
} else {
|
|
2527
2592
|
return errorResult("Provide either file_id, or both project_id and file_path.");
|
|
2528
2593
|
}
|
|
2529
|
-
const
|
|
2530
|
-
const file = await ctx.client.saveSpecFileRevision(fileId, content);
|
|
2594
|
+
const file = await ctx.client.saveSpecFileRevision(fileId, resolved.bytes);
|
|
2531
2595
|
return jsonResult({ file: specFileSummary(file) });
|
|
2532
2596
|
}
|
|
2533
2597
|
};
|
|
2534
2598
|
|
|
2535
2599
|
// src/server/tools/upload_attachment.ts
|
|
2536
2600
|
import { createHash as createHash2 } from "crypto";
|
|
2537
|
-
import { lstat, readFile } from "fs/promises";
|
|
2538
|
-
import { basename, extname, isAbsolute } from "path";
|
|
2601
|
+
import { lstat, readFile as readFile2 } from "fs/promises";
|
|
2602
|
+
import { basename, extname, isAbsolute as isAbsolute2 } from "path";
|
|
2539
2603
|
import { z as z12 } from "zod";
|
|
2540
2604
|
var DEFAULT_MIME = "application/octet-stream";
|
|
2541
2605
|
var MAX_ATTACHMENT_MB = MAX_ATTACHMENT_BYTES / (1024 * 1024);
|
|
@@ -2654,7 +2718,7 @@ var uploadAttachmentTool = {
|
|
|
2654
2718
|
description: `Upload a local file as a new attachment on a project. Reads \`file_path\` from the host running the MCP server, computes its SHA256, then performs the two-phase platform upload. Max ${String(MAX_ATTACHMENT_MB)} MB. Supported content: PDF, DOCX, XLSX, and any UTF-8 text file (XML, JSON, YAML, HTML, Markdown, CSV, source code, ...). Binary files outside the structured types will be rejected by the platform.`,
|
|
2655
2719
|
inputSchema: inputSchema12,
|
|
2656
2720
|
handler: async (args, ctx) => {
|
|
2657
|
-
if (!
|
|
2721
|
+
if (!isAbsolute2(args.file_path)) {
|
|
2658
2722
|
return errorResult(`file_path must be an absolute path; got "${args.file_path}".`);
|
|
2659
2723
|
}
|
|
2660
2724
|
let stats;
|
|
@@ -2690,7 +2754,7 @@ var uploadAttachmentTool = {
|
|
|
2690
2754
|
}
|
|
2691
2755
|
let buf;
|
|
2692
2756
|
try {
|
|
2693
|
-
buf = await
|
|
2757
|
+
buf = await readFile2(args.file_path);
|
|
2694
2758
|
} catch (err) {
|
|
2695
2759
|
const code = err.code;
|
|
2696
2760
|
if (code === "EACCES" || code === "EPERM") {
|
|
@@ -2794,15 +2858,16 @@ function validateSpecFilePath(filePath) {
|
|
|
2794
2858
|
if (segments.some((s) => s === "" || s === ".")) {
|
|
2795
2859
|
return 'file_path must be a normalized path (no empty, "." or trailing-slash segments).';
|
|
2796
2860
|
}
|
|
2797
|
-
|
|
2798
|
-
|
|
2861
|
+
const root = segments[0];
|
|
2862
|
+
if (root !== "specs" && root !== "openspec") {
|
|
2863
|
+
return "file_path must be rooted at 'specs' or 'openspec' (e.g. specs/changes/add-oauth-login/proposal.md).";
|
|
2799
2864
|
}
|
|
2800
2865
|
if (segments.length < 2) {
|
|
2801
|
-
return
|
|
2866
|
+
return `file_path must include a filename under '${root}' (e.g. ${root}/proposal.md).`;
|
|
2802
2867
|
}
|
|
2803
2868
|
const dirLevels = segments.length - 2;
|
|
2804
2869
|
if (dirLevels > MAX_SPEC_DIR_DEPTH) {
|
|
2805
|
-
return `file_path must not nest deeper than ${String(MAX_SPEC_DIR_DEPTH)} directory levels below
|
|
2870
|
+
return `file_path must not nest deeper than ${String(MAX_SPEC_DIR_DEPTH)} directory levels below the '${root}' root.`;
|
|
2806
2871
|
}
|
|
2807
2872
|
return null;
|
|
2808
2873
|
}
|
|
@@ -2831,20 +2896,32 @@ var SPEC_FILE_TYPES = [
|
|
|
2831
2896
|
var inputSchema13 = {
|
|
2832
2897
|
project_id: z13.string().min(1).describe("UUID of the project to upload the file into."),
|
|
2833
2898
|
file_path: z13.string().min(1).describe(
|
|
2834
|
-
'Path of the new spec file, rooted at "specs" with at most 3 directory levels below
|
|
2899
|
+
'Path of the new spec file, rooted at "specs" or "openspec" with at most 3 directory levels below the root (e.g. specs/changes/add-oauth-login/proposal.md).'
|
|
2900
|
+
),
|
|
2901
|
+
content: z13.string().min(1).optional().describe(
|
|
2902
|
+
"Full UTF-8 text content of the spec file. Provide this OR local_file_path (exactly one). Use for in-memory / generated content."
|
|
2903
|
+
),
|
|
2904
|
+
local_file_path: z13.string().min(1).optional().describe(
|
|
2905
|
+
"Path to a local file whose bytes become the spec file body; the server reads it directly. Provide this OR content (exactly one). Absolute paths are most reliable; a relative path resolves against the MCP server working directory. Point this only at an intended spec file \u2014 its bytes are uploaded to the project as-is; never use it for secrets or unrelated files."
|
|
2835
2906
|
),
|
|
2836
|
-
content: z13.string().min(1).describe("Full UTF-8 text content of the spec file (non-empty)."),
|
|
2837
2907
|
file_type: z13.enum(SPEC_FILE_TYPES).optional().describe("Spec file type. Omit to derive from the filename (e.g. proposal.md \u2192 proposal).")
|
|
2838
2908
|
};
|
|
2839
2909
|
var uploadSpecFileTool = {
|
|
2840
2910
|
name: "upload_spec_file",
|
|
2841
|
-
description: "Upload a new, session-less spec file into a project
|
|
2911
|
+
description: "Upload a new, session-less spec file into a project. Provide the file body inline via `content` or by pointing at a local file with `local_file_path` \u2014 exactly one is required. Prefer `local_file_path` when the body is already an existing local file (the server reads it directly, avoiding sending large content through the model); use `content` for text generated in-memory. Rejects if a file already exists at the same path (use update_spec_file to overwrite).",
|
|
2842
2912
|
inputSchema: inputSchema13,
|
|
2843
2913
|
handler: async (args, ctx) => {
|
|
2844
2914
|
const pathError = validateSpecFilePath(args.file_path);
|
|
2845
2915
|
if (pathError) {
|
|
2846
2916
|
return errorResult(pathError);
|
|
2847
2917
|
}
|
|
2918
|
+
const resolved = await resolveSpecContent({
|
|
2919
|
+
content: args.content,
|
|
2920
|
+
localFilePath: args.local_file_path
|
|
2921
|
+
});
|
|
2922
|
+
if (resolved.error || !resolved.bytes) {
|
|
2923
|
+
return errorResult(resolved.error ?? "Failed to resolve file content.");
|
|
2924
|
+
}
|
|
2848
2925
|
const existing = await ctx.client.findSpecFileByPath(args.project_id, args.file_path);
|
|
2849
2926
|
if (existing) {
|
|
2850
2927
|
return errorResult(
|
|
@@ -2852,12 +2929,11 @@ var uploadSpecFileTool = {
|
|
|
2852
2929
|
);
|
|
2853
2930
|
}
|
|
2854
2931
|
const fileType = args.file_type ?? deriveFileType(args.file_path);
|
|
2855
|
-
const content = new TextEncoder().encode(args.content);
|
|
2856
2932
|
const file = await ctx.client.uploadSpecFile({
|
|
2857
2933
|
projectId: args.project_id,
|
|
2858
2934
|
filePath: args.file_path,
|
|
2859
2935
|
fileType,
|
|
2860
|
-
content
|
|
2936
|
+
content: resolved.bytes
|
|
2861
2937
|
});
|
|
2862
2938
|
return jsonResult({ file: specFileSummary(file) });
|
|
2863
2939
|
}
|
|
@@ -2949,13 +3025,13 @@ var WebSocketClientTransport = class {
|
|
|
2949
3025
|
return Promise.resolve();
|
|
2950
3026
|
}
|
|
2951
3027
|
send(message) {
|
|
2952
|
-
return new Promise((
|
|
3028
|
+
return new Promise((resolve3, reject) => {
|
|
2953
3029
|
this.ws.send(JSON.stringify(message), (err) => {
|
|
2954
3030
|
if (err) {
|
|
2955
3031
|
reject(err);
|
|
2956
3032
|
return;
|
|
2957
3033
|
}
|
|
2958
|
-
|
|
3034
|
+
resolve3();
|
|
2959
3035
|
});
|
|
2960
3036
|
});
|
|
2961
3037
|
}
|
|
@@ -2989,7 +3065,7 @@ var WebSocketClientTransport = class {
|
|
|
2989
3065
|
|
|
2990
3066
|
// src/reverse/safe-path.ts
|
|
2991
3067
|
import { realpath } from "fs/promises";
|
|
2992
|
-
import { isAbsolute as
|
|
3068
|
+
import { isAbsolute as isAbsolute3, resolve as resolve2, sep } from "path";
|
|
2993
3069
|
var PathOutsideRootError = class extends Error {
|
|
2994
3070
|
constructor(userPath) {
|
|
2995
3071
|
super(`path_outside_root: ${userPath}`);
|
|
@@ -3007,10 +3083,10 @@ var AbsolutePathError = class extends Error {
|
|
|
3007
3083
|
userPath;
|
|
3008
3084
|
};
|
|
3009
3085
|
async function resolveSafePath(realRoot, userPath, opts = {}) {
|
|
3010
|
-
if (
|
|
3086
|
+
if (isAbsolute3(userPath)) {
|
|
3011
3087
|
throw new AbsolutePathError(userPath);
|
|
3012
3088
|
}
|
|
3013
|
-
const joined =
|
|
3089
|
+
const joined = resolve2(realRoot, userPath);
|
|
3014
3090
|
let resolved;
|
|
3015
3091
|
try {
|
|
3016
3092
|
resolved = await realpath(joined);
|
|
@@ -3115,7 +3191,7 @@ function ripgrepIgnoreGlobs() {
|
|
|
3115
3191
|
|
|
3116
3192
|
// src/reverse/pack-codebase.ts
|
|
3117
3193
|
import { createHash as createHash3 } from "crypto";
|
|
3118
|
-
import { mkdtemp, readFile as
|
|
3194
|
+
import { mkdtemp, readFile as readFile3, rm } from "fs/promises";
|
|
3119
3195
|
import { tmpdir } from "os";
|
|
3120
3196
|
import { join } from "path";
|
|
3121
3197
|
import { runCli } from "repomix";
|
|
@@ -3252,7 +3328,7 @@ async function invokeRepomix(exec, targetDir, args, timeoutMs) {
|
|
|
3252
3328
|
const execPromise = exec(["."], targetDir, options);
|
|
3253
3329
|
try {
|
|
3254
3330
|
await withTimeout(execPromise, timeoutMs);
|
|
3255
|
-
const content = await
|
|
3331
|
+
const content = await readFile3(outputFile, "utf-8");
|
|
3256
3332
|
await rm(workDir, { recursive: true, force: true });
|
|
3257
3333
|
return content;
|
|
3258
3334
|
} catch (err) {
|
|
@@ -3402,20 +3478,20 @@ async function handleRead(args, opts) {
|
|
|
3402
3478
|
return structuredError("path_ignored", `Path matches ignore rule ${check.rule ?? ""}`);
|
|
3403
3479
|
}
|
|
3404
3480
|
}
|
|
3405
|
-
let
|
|
3481
|
+
let stat2;
|
|
3406
3482
|
try {
|
|
3407
|
-
|
|
3483
|
+
stat2 = await fs4.stat(realPath);
|
|
3408
3484
|
} catch (err) {
|
|
3409
3485
|
return structuredError("stat_failed", errorMessage(err));
|
|
3410
3486
|
}
|
|
3411
|
-
if (!
|
|
3487
|
+
if (!stat2.isFile()) {
|
|
3412
3488
|
return structuredError("not_a_file", `Not a regular file: ${args.path}`);
|
|
3413
3489
|
}
|
|
3414
|
-
if (
|
|
3490
|
+
if (stat2.size > READ_FILE_MAX_BYTES) {
|
|
3415
3491
|
return structuredError(
|
|
3416
3492
|
"file_too_large",
|
|
3417
|
-
`File is ${String(
|
|
3418
|
-
{ sizeBytes:
|
|
3493
|
+
`File is ${String(stat2.size)} bytes; the read cap is ${String(READ_FILE_MAX_BYTES)} bytes. Use local_fs_grep to find the lines you need, or local_pack_codebase to pull the whole tree.`,
|
|
3494
|
+
{ sizeBytes: stat2.size, maxBytes: READ_FILE_MAX_BYTES }
|
|
3419
3495
|
);
|
|
3420
3496
|
}
|
|
3421
3497
|
let raw;
|
|
@@ -4020,7 +4096,7 @@ async function runOneConnection(opts) {
|
|
|
4020
4096
|
pongTimer = null;
|
|
4021
4097
|
}
|
|
4022
4098
|
});
|
|
4023
|
-
return new Promise((
|
|
4099
|
+
return new Promise((resolve3) => {
|
|
4024
4100
|
ws.once("close", (code, reasonBuf) => {
|
|
4025
4101
|
clearInterval(pingTimer);
|
|
4026
4102
|
if (pongTimer) clearTimeout(pongTimer);
|
|
@@ -4030,7 +4106,7 @@ async function runOneConnection(opts) {
|
|
|
4030
4106
|
`
|
|
4031
4107
|
);
|
|
4032
4108
|
const terminal = TERMINAL_CLOSE_CODES.has(code);
|
|
4033
|
-
|
|
4109
|
+
resolve3({
|
|
4034
4110
|
terminal,
|
|
4035
4111
|
reason: `code=${String(code)} ${reasonStr}`,
|
|
4036
4112
|
upMs: Date.now() - connectedAt,
|
|
@@ -4040,10 +4116,10 @@ async function runOneConnection(opts) {
|
|
|
4040
4116
|
});
|
|
4041
4117
|
}
|
|
4042
4118
|
function waitForOpen(ws) {
|
|
4043
|
-
return new Promise((
|
|
4119
|
+
return new Promise((resolve3, reject) => {
|
|
4044
4120
|
const onOpen = () => {
|
|
4045
4121
|
ws.removeListener("error", onError);
|
|
4046
|
-
|
|
4122
|
+
resolve3();
|
|
4047
4123
|
};
|
|
4048
4124
|
const onError = (err) => {
|
|
4049
4125
|
ws.removeListener("open", onOpen);
|
|
@@ -4054,7 +4130,7 @@ function waitForOpen(ws) {
|
|
|
4054
4130
|
});
|
|
4055
4131
|
}
|
|
4056
4132
|
function sleep2(ms) {
|
|
4057
|
-
return new Promise((
|
|
4133
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
4058
4134
|
}
|
|
4059
4135
|
|
|
4060
4136
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myspec/mcp-server",
|
|
3
|
-
"version": "0.1.2-next.
|
|
3
|
+
"version": "0.1.2-next.38",
|
|
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": {
|