devframe 0.7.16 → 0.8.0
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/bin/devframe.mjs +8 -0
- package/dist/adapters/build.d.mts +1 -1
- package/dist/adapters/build.mjs +4 -4
- package/dist/adapters/cac.d.mts +1 -1
- package/dist/adapters/cac.mjs +1 -1
- package/dist/adapters/cli.d.mts +1 -1
- package/dist/adapters/cli.mjs +1 -1
- package/dist/adapters/dev.d.mts +2 -2
- package/dist/adapters/dev.mjs +1 -1
- package/dist/adapters/embedded.d.mts +1 -1
- package/dist/adapters/mcp.d.mts +45 -3
- package/dist/adapters/mcp.mjs +2 -2
- package/dist/agent-tool-name-C3b5vEwJ.mjs +34 -0
- package/dist/{cac-CPaOy3LD.mjs → cac-B-R_tDGJ.mjs} +18 -13
- package/dist/cli/main.d.mts +11 -0
- package/dist/cli/main.mjs +283 -0
- package/dist/client/index.d.mts +4 -4
- package/dist/client/index.mjs +88 -4
- package/dist/{context-CzqPrJSz.d.mts → context-7BbaIUSI.d.mts} +1 -1
- package/dist/{context-DY72brRI.mjs → context-C9Cgm1hP.mjs} +1 -1
- package/dist/{dev-BCeaQuGE.mjs → dev-D4M7Veo7.mjs} +22 -11
- package/dist/{devframe-Ckhf3kFw.d.mts → devframe-Dsjn_Xtq.d.mts} +68 -15
- package/dist/diagnostics-B5-qHeqD.mjs +94 -0
- package/dist/{diagnostics-BXwBQmoN.mjs → diagnostics-hwjXp_UV.mjs} +8 -0
- package/dist/{dump-Cz7yVvsB.mjs → dump-CgZShDRB.mjs} +82 -6
- package/dist/{build-server-C7vTKjiQ.mjs → fetch-BZyK4v6W.mjs} +225 -51
- package/dist/helpers/vite.d.mts +1 -1
- package/dist/helpers/vite.mjs +2 -2
- package/dist/{host-h3-CgOetlNE.mjs → host-h3-Kz7t5Xab.mjs} +83 -28
- package/dist/http-BghOqfTD.mjs +38 -0
- package/dist/{index-8dndvIxR.d.mts → index-CeBbry0R.d.mts} +2 -2
- package/dist/{index-DgsLFhZg.d.mts → index-Dbw1p5ch.d.mts} +27 -3
- package/dist/{index-YDSJgsyG.d.mts → index-vAX6bYuc.d.mts} +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/instance-registry-D6fxYu36.mjs +190 -0
- package/dist/node/auth.d.mts +2 -2
- package/dist/node/hub-internals.d.mts +2 -2
- package/dist/node/hub-internals.mjs +1 -1
- package/dist/node/index.d.mts +104 -6
- package/dist/node/index.mjs +5 -4
- package/dist/recipes/common-rpc-functions.d.mts +33 -33
- package/dist/recipes/common-rpc-functions.mjs +6 -6
- package/dist/recipes/interactive-auth.d.mts +2 -2
- package/dist/recipes/interactive-auth.mjs +13 -13
- package/dist/rpc/dump.d.mts +1 -1
- package/dist/rpc/dump.mjs +1 -1
- package/dist/rpc/index.d.mts +3 -3
- package/dist/rpc/index.mjs +4 -4
- package/dist/rpc/transports/ws-client.d.mts +1 -1
- package/dist/rpc/transports/ws-client.mjs +1 -1
- package/dist/rpc/transports/ws-server.d.mts +1 -1
- package/dist/rpc/transports/ws-server.mjs +1 -1
- package/dist/{serialization-DpLXCy13.mjs → serialization-C8Mnw9hK.mjs} +1 -1
- package/dist/{server-DykHCza_.mjs → server-CcAPxuoT.mjs} +2 -2
- package/dist/{server-B0wiP_Oi.d.mts → server-VLQJouOO.d.mts} +2 -2
- package/dist/simple-schema-BDzLeJDk.d.mts +102 -0
- package/dist/simple-schema-DQPZrAaZ.mjs +154 -0
- package/dist/{storage-gEnj9ASQ.mjs → storage-Dzoc3NVC.mjs} +2 -66
- package/dist/{transports-BmDVn4SF.mjs → transports-vhizgqXM.mjs} +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/{types-CrzNxXKq.d.mts → types-CnJSgRVa.d.mts} +33 -19
- package/dist/utils/agent-tool-name.d.mts +27 -0
- package/dist/utils/agent-tool-name.mjs +34 -0
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/simple-schema.d.mts +2 -0
- package/dist/utils/simple-schema.mjs +154 -0
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/dist/{ws-client-DSXiI2h7.d.mts → ws-client-B6Tnr0ic.d.mts} +1 -1
- package/dist/{ws-server-9-wn7MNQ.d.mts → ws-server-D_Vjtums.d.mts} +1 -1
- package/package.json +18 -7
- package/skills/devframe/SKILL.md +1 -1
- package/dist/http-_pPbryTQ.mjs +0 -114
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/utils/agent-tool-name.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Derive the wire-safe agent tool name for an internal tool id.
|
|
4
|
+
*
|
|
5
|
+
* Devframe tool ids are colon-namespaced — `devframe:<area>:<fn>` for
|
|
6
|
+
* built-ins, `devframes:plugin:<slug>:<fn>` for plugin RPCs, and hub
|
|
7
|
+
* command ids for command-derived tools. MCP clients constrain tool names
|
|
8
|
+
* to `^[a-zA-Z0-9_-]{1,128}$`, so the agent/MCP boundary derives the wire
|
|
9
|
+
* name automatically: every run of characters outside `[a-zA-Z0-9_-]`
|
|
10
|
+
* becomes a single `_`, truncated to 128 characters. Internal ids never
|
|
11
|
+
* change — resolution back to the id happens at the boundary.
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
* devframe:state:read → devframe_state_read
|
|
15
|
+
* devframes:plugin:git:status → devframes_plugin_git_status
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* A plain string transform with no node dependency, so browser-side UIs
|
|
19
|
+
* that display a tool's id (e.g. the inspect plugin's agent view) can
|
|
20
|
+
* import it too and show the name a client actually calls.
|
|
21
|
+
*
|
|
22
|
+
* @experimental The agent-native surface is experimental and may change
|
|
23
|
+
* without a major version bump until it stabilizes.
|
|
24
|
+
*/
|
|
25
|
+
declare function toAgentToolName(id: string): string;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { toAgentToolName };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/utils/agent-tool-name.ts
|
|
2
|
+
/**
|
|
3
|
+
* Maximum tool-name length several MCP clients enforce (the Anthropic API
|
|
4
|
+
* pattern is `^[a-zA-Z0-9_-]{1,128}$`).
|
|
5
|
+
*/
|
|
6
|
+
const MAX_TOOL_NAME_LENGTH = 128;
|
|
7
|
+
/**
|
|
8
|
+
* Derive the wire-safe agent tool name for an internal tool id.
|
|
9
|
+
*
|
|
10
|
+
* Devframe tool ids are colon-namespaced — `devframe:<area>:<fn>` for
|
|
11
|
+
* built-ins, `devframes:plugin:<slug>:<fn>` for plugin RPCs, and hub
|
|
12
|
+
* command ids for command-derived tools. MCP clients constrain tool names
|
|
13
|
+
* to `^[a-zA-Z0-9_-]{1,128}$`, so the agent/MCP boundary derives the wire
|
|
14
|
+
* name automatically: every run of characters outside `[a-zA-Z0-9_-]`
|
|
15
|
+
* becomes a single `_`, truncated to 128 characters. Internal ids never
|
|
16
|
+
* change — resolution back to the id happens at the boundary.
|
|
17
|
+
*
|
|
18
|
+
* ```
|
|
19
|
+
* devframe:state:read → devframe_state_read
|
|
20
|
+
* devframes:plugin:git:status → devframes_plugin_git_status
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* A plain string transform with no node dependency, so browser-side UIs
|
|
24
|
+
* that display a tool's id (e.g. the inspect plugin's agent view) can
|
|
25
|
+
* import it too and show the name a client actually calls.
|
|
26
|
+
*
|
|
27
|
+
* @experimental The agent-native surface is experimental and may change
|
|
28
|
+
* without a major version bump until it stabilizes.
|
|
29
|
+
*/
|
|
30
|
+
function toAgentToolName(id) {
|
|
31
|
+
return id.replace(/[^\w-]+/g, "_").slice(0, MAX_TOOL_NAME_LENGTH);
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { toAgentToolName };
|
package/dist/utils/events.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _t as SharedStateOptions, dt as ImmutableArray, ft as ImmutableMap, gt as SharedStateEvents, ht as SharedState, mt as ImmutableSet, pt as ImmutableObject, ut as Immutable, vt as SharedStatePatch, yt as createSharedState } from "../devframe-
|
|
1
|
+
import { _t as SharedStateOptions, dt as ImmutableArray, ft as ImmutableMap, gt as SharedStateEvents, ht as SharedState, mt as ImmutableSet, pt as ImmutableObject, ut as Immutable, vt as SharedStatePatch, yt as createSharedState } from "../devframe-Dsjn_Xtq.mjs";
|
|
2
2
|
export { Immutable, ImmutableArray, ImmutableMap, ImmutableObject, ImmutableSet, SharedState, SharedStateEvents, SharedStateOptions, SharedStatePatch, createSharedState };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as literal, c as number, d as picklist, f as record, g as voidType, h as union, i as describe, l as object, m as string, n as array, o as nullType, p as s, r as boolean, s as nullable, t as SimpleSchema, u as optional } from "../simple-schema-BDzLeJDk.mjs";
|
|
2
|
+
export { SimpleSchema, array, boolean, describe, literal, nullType, nullable, number, object, optional, picklist, record, s, string, union, voidType };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
//#region src/utils/simple-schema.ts
|
|
2
|
+
function ok(value) {
|
|
3
|
+
return { value };
|
|
4
|
+
}
|
|
5
|
+
function fail(message, path) {
|
|
6
|
+
return { issues: [path ? {
|
|
7
|
+
message,
|
|
8
|
+
path
|
|
9
|
+
} : { message }] };
|
|
10
|
+
}
|
|
11
|
+
function make(type, validate, extra) {
|
|
12
|
+
return {
|
|
13
|
+
type,
|
|
14
|
+
...extra,
|
|
15
|
+
"~standard": {
|
|
16
|
+
version: 1,
|
|
17
|
+
vendor: "devframe",
|
|
18
|
+
validate
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Run a Standard Schema synchronously, rejecting async validators. */
|
|
23
|
+
function runSync(schema, value) {
|
|
24
|
+
const result = schema["~standard"].validate(value);
|
|
25
|
+
if (result instanceof Promise) throw new TypeError("[devframe/utils/simple-schema] async validators are not supported inside object()/optional()/nullable()");
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
/** Any string. */
|
|
29
|
+
function string() {
|
|
30
|
+
return make("string", (v) => typeof v === "string" ? ok(v) : fail("Expected a string"));
|
|
31
|
+
}
|
|
32
|
+
/** A finite number (rejects `NaN`). */
|
|
33
|
+
function number() {
|
|
34
|
+
return make("number", (v) => typeof v === "number" && !Number.isNaN(v) ? ok(v) : fail("Expected a number"));
|
|
35
|
+
}
|
|
36
|
+
/** A boolean. */
|
|
37
|
+
function boolean() {
|
|
38
|
+
return make("boolean", (v) => typeof v === "boolean" ? ok(v) : fail("Expected a boolean"));
|
|
39
|
+
}
|
|
40
|
+
/** `undefined` — mirrors valibot's `void`. */
|
|
41
|
+
function voidType() {
|
|
42
|
+
return make("void", (v) => v === void 0 ? ok(void 0) : fail("Expected undefined"));
|
|
43
|
+
}
|
|
44
|
+
/** `null`. */
|
|
45
|
+
function nullType() {
|
|
46
|
+
return make("null", (v) => v === null ? ok(null) : fail("Expected null"));
|
|
47
|
+
}
|
|
48
|
+
/** One of a fixed set of literal values. */
|
|
49
|
+
function picklist(values) {
|
|
50
|
+
const set = new Set(values);
|
|
51
|
+
return make("picklist", (v) => set.has(v) ? ok(v) : fail(`Expected one of: ${values.join(", ")}`), { values });
|
|
52
|
+
}
|
|
53
|
+
/** A single literal value (string / number / boolean). */
|
|
54
|
+
function literal(value) {
|
|
55
|
+
return make("literal", (v) => v === value ? ok(v) : fail(`Expected ${JSON.stringify(value)}`), { value });
|
|
56
|
+
}
|
|
57
|
+
/** A value matching any one of the given schemas. */
|
|
58
|
+
function union(options) {
|
|
59
|
+
return make("union", (v) => {
|
|
60
|
+
const issues = [];
|
|
61
|
+
for (const option of options) {
|
|
62
|
+
const result = runSync(option, v);
|
|
63
|
+
if (!result.issues) return ok(v);
|
|
64
|
+
issues.push(...result.issues);
|
|
65
|
+
}
|
|
66
|
+
return { issues };
|
|
67
|
+
}, { options });
|
|
68
|
+
}
|
|
69
|
+
/** A record with string keys whose values each satisfy the value schema. */
|
|
70
|
+
function record(_key, value) {
|
|
71
|
+
return make("record", (v) => {
|
|
72
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return fail("Expected an object");
|
|
73
|
+
const obj = v;
|
|
74
|
+
const issues = [];
|
|
75
|
+
for (const key of Object.keys(obj)) {
|
|
76
|
+
const result = runSync(value, obj[key]);
|
|
77
|
+
if (result.issues) for (const issue of result.issues) issues.push({
|
|
78
|
+
message: issue.message,
|
|
79
|
+
path: [key, ...issue.path ?? []]
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return issues.length ? { issues } : ok(v);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/** An array whose every element satisfies the item schema. */
|
|
86
|
+
function array(item) {
|
|
87
|
+
return make("array", (v) => {
|
|
88
|
+
if (!Array.isArray(v)) return fail("Expected an array");
|
|
89
|
+
const issues = [];
|
|
90
|
+
for (let i = 0; i < v.length; i++) {
|
|
91
|
+
const result = runSync(item, v[i]);
|
|
92
|
+
if (result.issues) for (const issue of result.issues) issues.push({
|
|
93
|
+
message: issue.message,
|
|
94
|
+
path: [i, ...issue.path ?? []]
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return issues.length ? { issues } : ok(v);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/** An object whose known keys each satisfy their schema (extra keys are kept). */
|
|
101
|
+
function object(shape) {
|
|
102
|
+
const entries = Object.entries(shape);
|
|
103
|
+
return make("object", (v) => {
|
|
104
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return fail("Expected an object");
|
|
105
|
+
const obj = v;
|
|
106
|
+
const issues = [];
|
|
107
|
+
for (const [key, schema] of entries) {
|
|
108
|
+
const result = runSync(schema, obj[key]);
|
|
109
|
+
if (result.issues) for (const issue of result.issues) issues.push({
|
|
110
|
+
message: issue.message,
|
|
111
|
+
path: [key, ...issue.path ?? []]
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return issues.length ? { issues } : ok(v);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/** Allow `undefined` in addition to the inner schema. */
|
|
118
|
+
function optional(inner) {
|
|
119
|
+
return make("optional", (v) => v === void 0 ? ok(void 0) : runSync(inner, v), { wrapped: inner });
|
|
120
|
+
}
|
|
121
|
+
/** Allow `null` in addition to the inner schema. */
|
|
122
|
+
function nullable(inner) {
|
|
123
|
+
return make("nullable", (v) => v === null ? ok(null) : runSync(inner, v), { wrapped: inner });
|
|
124
|
+
}
|
|
125
|
+
/** Attach a human-readable description (used for CLI option help). */
|
|
126
|
+
function describe(schema, description) {
|
|
127
|
+
return {
|
|
128
|
+
...schema,
|
|
129
|
+
description
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Grouped access to every builder — `s.string()`, `s.object({ ... })`,
|
|
134
|
+
* `s.void()`, etc. Handy for a valibot-like `import { s } from
|
|
135
|
+
* 'devframe/utils/simple-schema'` call site.
|
|
136
|
+
*/
|
|
137
|
+
const s = {
|
|
138
|
+
string,
|
|
139
|
+
number,
|
|
140
|
+
boolean,
|
|
141
|
+
void: voidType,
|
|
142
|
+
null: nullType,
|
|
143
|
+
literal,
|
|
144
|
+
picklist,
|
|
145
|
+
union,
|
|
146
|
+
record,
|
|
147
|
+
array,
|
|
148
|
+
object,
|
|
149
|
+
optional,
|
|
150
|
+
nullable,
|
|
151
|
+
describe
|
|
152
|
+
};
|
|
153
|
+
//#endregion
|
|
154
|
+
export { array, boolean, describe, literal, nullType, nullable, number, object, optional, picklist, record, s, string, union, voidType };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { at as StreamReader, ct as createStreamReader, it as StreamErrorPayload, lt as createStreamSink, nt as CreateStreamReaderOptions, ot as StreamSink, rt as CreateStreamSinkOptions, st as StreamSinkEvents, tt as BufferedChunk } from "../devframe-
|
|
1
|
+
import { at as StreamReader, ct as createStreamReader, it as StreamErrorPayload, lt as createStreamSink, nt as CreateStreamReaderOptions, ot as StreamSink, rt as CreateStreamSinkOptions, st as StreamSinkEvents, tt as BufferedChunk } from "../devframe-Dsjn_Xtq.mjs";
|
|
2
2
|
export { BufferedChunk, CreateStreamReaderOptions, CreateStreamSinkOptions, StreamErrorPayload, StreamReader, StreamSink, StreamSinkEvents, createStreamReader, createStreamSink };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { v as RpcFunctionDefinitionAny } from "./types-
|
|
1
|
+
import { v as RpcFunctionDefinitionAny } from "./types-CnJSgRVa.mjs";
|
|
2
2
|
import { Peer } from "crossws";
|
|
3
3
|
import { BirpcGroup, ChannelOptions } from "birpc";
|
|
4
4
|
import { NodeAdapter } from "crossws/adapters/node";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devframe",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"description": "Framework for building generic devframes",
|
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"./rpc/transports/ws-client": "./dist/rpc/transports/ws-client.mjs",
|
|
43
43
|
"./rpc/transports/ws-server": "./dist/rpc/transports/ws-server.mjs",
|
|
44
44
|
"./types": "./dist/types/index.mjs",
|
|
45
|
+
"./utils/agent-tool-name": "./dist/utils/agent-tool-name.mjs",
|
|
45
46
|
"./utils/colors": "./dist/utils/colors.mjs",
|
|
46
47
|
"./utils/crypto-token": "./dist/utils/crypto-token.mjs",
|
|
47
48
|
"./utils/events": "./dist/utils/events.mjs",
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
"./utils/nanoid": "./dist/utils/nanoid.mjs",
|
|
51
52
|
"./utils/open": "./dist/utils/open.mjs",
|
|
52
53
|
"./utils/promise": "./dist/utils/promise.mjs",
|
|
54
|
+
"./utils/simple-schema": "./dist/utils/simple-schema.mjs",
|
|
53
55
|
"./utils/scope": "./dist/utils/scope.mjs",
|
|
54
56
|
"./utils/serve-static": "./dist/utils/serve-static.mjs",
|
|
55
57
|
"./utils/shared-state": "./dist/utils/shared-state.mjs",
|
|
@@ -59,16 +61,24 @@
|
|
|
59
61
|
"./package.json": "./package.json"
|
|
60
62
|
},
|
|
61
63
|
"types": "./dist/index.d.mts",
|
|
64
|
+
"bin": {
|
|
65
|
+
"devframe": "./bin/devframe.mjs"
|
|
66
|
+
},
|
|
62
67
|
"files": [
|
|
68
|
+
"bin",
|
|
63
69
|
"dist",
|
|
64
70
|
"skills"
|
|
65
71
|
],
|
|
66
72
|
"peerDependencies": {
|
|
67
|
-
"@modelcontextprotocol/
|
|
73
|
+
"@modelcontextprotocol/client": "^2.0.0",
|
|
74
|
+
"@modelcontextprotocol/server": "^2.0.0",
|
|
68
75
|
"cac": "^7.0.0"
|
|
69
76
|
},
|
|
70
77
|
"peerDependenciesMeta": {
|
|
71
|
-
"@modelcontextprotocol/
|
|
78
|
+
"@modelcontextprotocol/client": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"@modelcontextprotocol/server": {
|
|
72
82
|
"optional": true
|
|
73
83
|
},
|
|
74
84
|
"cac": {
|
|
@@ -76,7 +86,7 @@
|
|
|
76
86
|
}
|
|
77
87
|
},
|
|
78
88
|
"dependencies": {
|
|
79
|
-
"@
|
|
89
|
+
"@standard-schema/spec": "^1.1.0",
|
|
80
90
|
"birpc": "^4.0.0",
|
|
81
91
|
"crossws": "^0.4.10",
|
|
82
92
|
"destr": "^2.0.5",
|
|
@@ -84,11 +94,11 @@
|
|
|
84
94
|
"mrmime": "^2.0.1",
|
|
85
95
|
"nostics": "^1.2.0",
|
|
86
96
|
"pathe": "^2.0.3",
|
|
87
|
-
"ufo": "^1.6.4"
|
|
88
|
-
"valibot": "^1.4.2"
|
|
97
|
+
"ufo": "^1.6.4"
|
|
89
98
|
},
|
|
90
99
|
"devDependencies": {
|
|
91
|
-
"@modelcontextprotocol/
|
|
100
|
+
"@modelcontextprotocol/client": "^2.0.0",
|
|
101
|
+
"@modelcontextprotocol/server": "^2.0.0",
|
|
92
102
|
"cac": "^7.0.0",
|
|
93
103
|
"get-port-please": "^3.2.0",
|
|
94
104
|
"immer": "^11.1.15",
|
|
@@ -103,6 +113,7 @@
|
|
|
103
113
|
"tinyglobby": "^0.2.17",
|
|
104
114
|
"tsdown": "^0.22.14",
|
|
105
115
|
"ua-parser-modern": "^0.1.1",
|
|
116
|
+
"valibot": "^1.4.2",
|
|
106
117
|
"whenexpr": "^0.1.2",
|
|
107
118
|
"ws": "^8.21.1"
|
|
108
119
|
},
|
package/skills/devframe/SKILL.md
CHANGED
|
@@ -485,7 +485,7 @@ import { createMcpServer } from 'devframe/adapters/mcp'
|
|
|
485
485
|
await createMcpServer(devframe, { transport: 'stdio' })
|
|
486
486
|
```
|
|
487
487
|
|
|
488
|
-
`@modelcontextprotocol/
|
|
488
|
+
`@modelcontextprotocol/server` is a peer dependency. The CLI adapter also exposes `my-devframe mcp` — route host logs to stderr (stdout is the MCP transport). Safety classifications (`'read' | 'action' | 'destructive'`) drive MCP hint annotations that agent clients use to prompt for confirmation.
|
|
489
489
|
|
|
490
490
|
## Author SPA
|
|
491
491
|
|
package/dist/http-_pPbryTQ.mjs
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { isAllowedOrigin } from "./rpc/transports/ws-server.mjs";
|
|
2
|
-
import { t as buildMcpServerFromContext } from "./build-server-C7vTKjiQ.mjs";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { defineHandler } from "h3";
|
|
5
|
-
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
-
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
7
|
-
//#region src/adapters/mcp/http.ts
|
|
8
|
-
/**
|
|
9
|
-
* Mount an MCP Streamable-HTTP endpoint on an h3 app at `path`.
|
|
10
|
-
*
|
|
11
|
-
* Each MCP session gets its own {@link WebStandardStreamableHTTPServerTransport}
|
|
12
|
-
* and MCP server (built from the shared, live `ctx` via
|
|
13
|
-
* `buildMcpServerFromContext`), correlated by the `Mcp-Session-Id` header:
|
|
14
|
-
* an `initialize` POST spins up a session; later requests route to it; a
|
|
15
|
-
* `DELETE` (or client disconnect) tears it down.
|
|
16
|
-
*
|
|
17
|
-
* The transport is web-standard — its `handleRequest` takes the h3 event's
|
|
18
|
-
* web `Request` and returns a web `Response` (an SSE `ReadableStream` body
|
|
19
|
-
* for the server→client stream). We copy that response onto `event.res` and
|
|
20
|
-
* return its body rather than returning the `Response` object directly, so a
|
|
21
|
-
* legitimate MCP 404 (unknown session) isn't swallowed by h3's
|
|
22
|
-
* "Response-with-404 falls through to the next handler" rule (which would
|
|
23
|
-
* otherwise hand the request to the SPA static catch-all).
|
|
24
|
-
*
|
|
25
|
-
* @experimental
|
|
26
|
-
*/
|
|
27
|
-
function mountMcpHttp(app, ctx, path, options) {
|
|
28
|
-
const sessions = /* @__PURE__ */ new Map();
|
|
29
|
-
const allowedOrigins = options.allowedOrigins;
|
|
30
|
-
function drop(sessionId) {
|
|
31
|
-
const session = sessions.get(sessionId);
|
|
32
|
-
if (!session) return;
|
|
33
|
-
sessions.delete(sessionId);
|
|
34
|
-
session.dispose();
|
|
35
|
-
}
|
|
36
|
-
async function createSession() {
|
|
37
|
-
let session;
|
|
38
|
-
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
39
|
-
sessionIdGenerator: () => randomUUID(),
|
|
40
|
-
onsessioninitialized: (id) => {
|
|
41
|
-
sessions.set(id, session);
|
|
42
|
-
},
|
|
43
|
-
onsessionclosed: (id) => {
|
|
44
|
-
drop(id);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
const { server, dispose } = buildMcpServerFromContext(ctx, {
|
|
48
|
-
serverName: options.serverName,
|
|
49
|
-
serverVersion: options.serverVersion,
|
|
50
|
-
exposeSharedState: options.exposeSharedState
|
|
51
|
-
});
|
|
52
|
-
session = {
|
|
53
|
-
transport,
|
|
54
|
-
dispose: async () => {
|
|
55
|
-
dispose();
|
|
56
|
-
await server.close();
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
transport.onclose = () => {
|
|
60
|
-
if (transport.sessionId) drop(transport.sessionId);
|
|
61
|
-
};
|
|
62
|
-
await server.connect(transport);
|
|
63
|
-
return session;
|
|
64
|
-
}
|
|
65
|
-
app.use(path, defineHandler(async (event) => {
|
|
66
|
-
const req = event.req;
|
|
67
|
-
const origin = req.headers.get("origin") ?? void 0;
|
|
68
|
-
if (allowedOrigins !== false && !isAllowedOrigin(origin, allowedOrigins ?? [])) {
|
|
69
|
-
event.res.status = 403;
|
|
70
|
-
return "Forbidden: origin not allowed";
|
|
71
|
-
}
|
|
72
|
-
const sessionId = req.headers.get("mcp-session-id") ?? void 0;
|
|
73
|
-
let session = sessionId ? sessions.get(sessionId) : void 0;
|
|
74
|
-
if (!session && req.method === "POST") {
|
|
75
|
-
let body;
|
|
76
|
-
try {
|
|
77
|
-
body = await req.json();
|
|
78
|
-
} catch {
|
|
79
|
-
body = void 0;
|
|
80
|
-
}
|
|
81
|
-
if (!sessionId && isInitializeRequest(body)) session = await createSession();
|
|
82
|
-
else {
|
|
83
|
-
event.res.status = sessionId ? 404 : 400;
|
|
84
|
-
return sessionId ? "Not Found: unknown MCP session" : "Bad Request: no valid session ID and not an initialize request";
|
|
85
|
-
}
|
|
86
|
-
return respond(event, await session.transport.handleRequest(req, { parsedBody: body }));
|
|
87
|
-
}
|
|
88
|
-
if (!session) {
|
|
89
|
-
event.res.status = sessionId ? 404 : 400;
|
|
90
|
-
return sessionId ? "Not Found: unknown MCP session" : "Bad Request: missing MCP session ID";
|
|
91
|
-
}
|
|
92
|
-
return respond(event, await session.transport.handleRequest(req));
|
|
93
|
-
}));
|
|
94
|
-
return { dispose: async () => {
|
|
95
|
-
const live = [...sessions.values()];
|
|
96
|
-
sessions.clear();
|
|
97
|
-
await Promise.all(live.map((session) => session.dispose()));
|
|
98
|
-
} };
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Copy a web `Response` from the MCP transport onto the h3 event's response
|
|
102
|
-
* and return its body. Returning the body (a `ReadableStream` or `null`)
|
|
103
|
-
* rather than the `Response` object avoids h3's 404-fall-through behavior.
|
|
104
|
-
*/
|
|
105
|
-
function respond(event, response) {
|
|
106
|
-
event.res.status = response.status;
|
|
107
|
-
event.res.statusText = response.statusText;
|
|
108
|
-
response.headers.forEach((value, key) => {
|
|
109
|
-
event.res.headers.set(key, value);
|
|
110
|
-
});
|
|
111
|
-
return response.body ?? "";
|
|
112
|
-
}
|
|
113
|
-
//#endregion
|
|
114
|
-
export { mountMcpHttp };
|