surfil 3.6.7
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 +68 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +749 -0
- package/dist/index.js.map +1 -0
- package/dist/retrieve.d.ts +70 -0
- package/dist/retrieve.d.ts.map +1 -0
- package/dist/retrieve.js +147 -0
- package/dist/retrieve.js.map +1 -0
- package/dist/temper-standard.d.ts +17 -0
- package/dist/temper-standard.d.ts.map +1 -0
- package/dist/temper-standard.js +137 -0
- package/dist/temper-standard.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# surfil-mcp
|
|
2
|
+
|
|
3
|
+
> **Note:** The live npm package name is `@surfil/mcp` (the canonical npm package is now scoped under the `surfil` org · it will be restored as the canonical name once the lock expires). Install with `npm i -g @surfil/mcp` or use `npx -y @surfil/mcp@latest`.
|
|
4
|
+
|
|
5
|
+
**Surfil MCP server** — published on npm as `surfil-mcp` for automatic AI session tracking across official Surfil adapters.
|
|
6
|
+
|
|
7
|
+
Once connected, Surfil automatically:
|
|
8
|
+
- Opens a session when your AI tool starts
|
|
9
|
+
- Tracks tool usage throughout the session
|
|
10
|
+
- Closes the session when your AI tool exits
|
|
11
|
+
- Displays everything on your [Surfil dashboard](https://surfil.com/dashboard)
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
`surfil-mcp` is normally installed for you automatically via the Surfil CLI:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
surfil connect <tool>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Supported official adapters:
|
|
22
|
+
- `claude-code`
|
|
23
|
+
- `cursor`
|
|
24
|
+
- `vscode`
|
|
25
|
+
- `windsurf`
|
|
26
|
+
- `jetbrains`
|
|
27
|
+
- `opencode`
|
|
28
|
+
- `neovim`
|
|
29
|
+
- `copilot`
|
|
30
|
+
- `continue`
|
|
31
|
+
- `codex`
|
|
32
|
+
- `gemini-cli`
|
|
33
|
+
- `aider`
|
|
34
|
+
- `zed`
|
|
35
|
+
- `warp`
|
|
36
|
+
- `roo`
|
|
37
|
+
- `cline`
|
|
38
|
+
- `powershell`
|
|
39
|
+
|
|
40
|
+
## Manual MCP config
|
|
41
|
+
|
|
42
|
+
If you need to wire it into another MCP-compatible tool manually, add this server entry:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["-y", "@surfil/mcp@latest"]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Surfil-generated configs handle `SURFIL_KEYSTORE=file://` automatically when a tool needs the file-backed keystore path.
|
|
52
|
+
|
|
53
|
+
## How it works
|
|
54
|
+
|
|
55
|
+
- **Session start** → emitted automatically when the MCP server initializes
|
|
56
|
+
- **Session end** → emitted automatically when the AI tool disconnects
|
|
57
|
+
- **Tool tracking** → emitted as MCP-aware tools invoke Surfil-managed actions
|
|
58
|
+
- **Goal and attribution** → attached to the current Surfil device, session, and project context
|
|
59
|
+
|
|
60
|
+
## Requirements
|
|
61
|
+
|
|
62
|
+
- Node.js ≥ 18
|
|
63
|
+
- Surfil account at [surfil.com](https://surfil.com)
|
|
64
|
+
- `surfil` CLI activated with a valid license token
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
See [LICENSE](../LICENSE) — UpgradIQ, Inc.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Surfil MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Runs as an MCP stdio server. If a session was started with `surfil on`, this
|
|
6
|
+
* server attaches to that session. Otherwise it creates its own session and
|
|
7
|
+
* emits SessionStart/SessionEnd. Exposes tools: surfil_set_goal, surfil_status,
|
|
8
|
+
* surfil_track.
|
|
9
|
+
*
|
|
10
|
+
* Uses the surfil binary from PATH — same binary that Claude Code hooks use.
|
|
11
|
+
*/
|
|
12
|
+
declare function emitSignal(event: string, tool: string, extraArgs?: string[]): void;
|
|
13
|
+
declare function handleCallTool(req: {
|
|
14
|
+
params: {
|
|
15
|
+
name: string;
|
|
16
|
+
arguments?: unknown;
|
|
17
|
+
};
|
|
18
|
+
}): Promise<{
|
|
19
|
+
content: Array<{
|
|
20
|
+
type: string;
|
|
21
|
+
text: string;
|
|
22
|
+
}>;
|
|
23
|
+
isError?: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
export declare const __test: {
|
|
26
|
+
emitSignal: typeof emitSignal;
|
|
27
|
+
signalCount: () => number;
|
|
28
|
+
reset(id: string): void;
|
|
29
|
+
handleCallTool: typeof handleCallTool;
|
|
30
|
+
getGoal: () => string | null;
|
|
31
|
+
};
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AAuJH,iBAAS,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAAM,EAAO,GAAG,IAAI,CAsB/E;AA8WD,iBAAe,cAAc,CAAC,GAAG,EAAE;IAAE,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CA8GpK;AAmED,eAAO,MAAM,MAAM;;;cAGP,MAAM;;;CAMjB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Surfil MCP Server
|
|
5
|
+
*
|
|
6
|
+
* Runs as an MCP stdio server. If a session was started with `surfil on`, this
|
|
7
|
+
* server attaches to that session. Otherwise it creates its own session and
|
|
8
|
+
* emits SessionStart/SessionEnd. Exposes tools: surfil_set_goal, surfil_status,
|
|
9
|
+
* surfil_track.
|
|
10
|
+
*
|
|
11
|
+
* Uses the surfil binary from PATH — same binary that Claude Code hooks use.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.__test = void 0;
|
|
48
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
49
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
50
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
51
|
+
const temper_standard_js_1 = require("./temper-standard.js");
|
|
52
|
+
const crypto = __importStar(require("crypto"));
|
|
53
|
+
const cp = __importStar(require("child_process"));
|
|
54
|
+
const fs = __importStar(require("fs"));
|
|
55
|
+
const os = __importStar(require("os"));
|
|
56
|
+
const path = __importStar(require("path"));
|
|
57
|
+
// ── Active session discovery ──────────────────────────────────────────────────
|
|
58
|
+
const SESSION_MAX_AGE_MS = 8 * 60 * 60 * 1000; // manual-session stale guard (8h)
|
|
59
|
+
const ADAPTER_FRESH_SKEW_MS = 120 * 1000; // adapter session must be this fresh vs our start
|
|
60
|
+
/**
|
|
61
|
+
* Authoritative per-process session id exposed by some adapters via env.
|
|
62
|
+
* GitHub Copilot CLI sets COPILOT_AGENT_SESSION_ID to the exact session id its
|
|
63
|
+
* lifecycle hooks use (verified on v1.0.59). Reading it is race-free and stays
|
|
64
|
+
* correct under parallel sessions — each Copilot process gets its own env —
|
|
65
|
+
* unlike the shared session.json file. Returns "" when no authoritative env id
|
|
66
|
+
* is available, so callers fall back to session.json discovery.
|
|
67
|
+
*/
|
|
68
|
+
function adapterEnvSessionId() {
|
|
69
|
+
const id = process.env["COPILOT_AGENT_SESSION_ID"];
|
|
70
|
+
return id && id.trim() !== "" ? id.trim() : "";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Read the active session from the surfil state directory (session.json), written
|
|
74
|
+
* by the surfil binary on `surfil on` or an adapter SessionStart hook. Returns null
|
|
75
|
+
* when the file is missing, partial, or invalid JSON (all treated as transient
|
|
76
|
+
* during the resolve poll). Attach eligibility is decided separately by
|
|
77
|
+
* isAttachable() so the freshness/source rules live in one place.
|
|
78
|
+
*/
|
|
79
|
+
function readActiveSession() {
|
|
80
|
+
const stateDir = process.env["XDG_DATA_HOME"]
|
|
81
|
+
? path.join(process.env["XDG_DATA_HOME"], "surfil")
|
|
82
|
+
: path.join(os.homedir(), ".local", "share", "surfil");
|
|
83
|
+
const sessionFile = path.join(stateDir, "session.json");
|
|
84
|
+
try {
|
|
85
|
+
const data = fs.readFileSync(sessionFile, "utf8");
|
|
86
|
+
const parsed = JSON.parse(data);
|
|
87
|
+
if (!parsed.session_id)
|
|
88
|
+
return null;
|
|
89
|
+
const startedAtMs = parsed.started_at ? new Date(parsed.started_at).getTime() : NaN;
|
|
90
|
+
const lastSignalMs = parsed.last_signal_at ? new Date(parsed.last_signal_at).getTime() : NaN;
|
|
91
|
+
return {
|
|
92
|
+
sessionId: parsed.session_id,
|
|
93
|
+
goal: parsed.goal,
|
|
94
|
+
source: parsed.source ?? "",
|
|
95
|
+
startedAtMs: Number.isNaN(startedAtMs) ? 0 : startedAtMs,
|
|
96
|
+
lastSignalMs: Number.isNaN(lastSignalMs) ? 0 : lastSignalMs,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
catch { /* missing / partial / invalid json — transient during poll */ }
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Decide whether the on-disk session belongs to this MCP process so we can
|
|
104
|
+
* attach (forward goal/track) instead of minting a competing session.
|
|
105
|
+
* - Manual `surfil on` (or a legacy binary that does not tag source): attach
|
|
106
|
+
* within the 8h stale window — manual attachment is intentional.
|
|
107
|
+
* - Adapter-tagged session: attach only when it matches our own client tool
|
|
108
|
+
* AND is the live current run (started/last-signalled around our start), so
|
|
109
|
+
* we never inherit a different tool's session or a stale crashed one.
|
|
110
|
+
*/
|
|
111
|
+
function isAttachable(a) {
|
|
112
|
+
if (!a.sessionId)
|
|
113
|
+
return false;
|
|
114
|
+
if (a.startedAtMs > 0 && Date.now() - a.startedAtMs > SESSION_MAX_AGE_MS)
|
|
115
|
+
return false;
|
|
116
|
+
if (a.source === "")
|
|
117
|
+
return true;
|
|
118
|
+
if (a.source === TOOL_NAME) {
|
|
119
|
+
return a.startedAtMs >= MCP_START_MS - ADAPTER_FRESH_SKEW_MS
|
|
120
|
+
|| a.lastSignalMs >= MCP_START_MS - ADAPTER_FRESH_SKEW_MS;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
// ── Session state ─────────────────────────────────────────────────────────────
|
|
125
|
+
//
|
|
126
|
+
// Session identity is resolved LAZILY, not at module load. Resolving at load
|
|
127
|
+
// races the adapter's SessionStart hook (which writes session.json a moment
|
|
128
|
+
// after Copilot spawns this server) and, when it loses, mints a competing
|
|
129
|
+
// session id — producing a duplicate session row. Instead we resolve on first
|
|
130
|
+
// use: a non-owning probe at Initialize, and a full resolve (attach-or-own)
|
|
131
|
+
// right before the first emitted signal.
|
|
132
|
+
const MCP_START_MS = Date.now();
|
|
133
|
+
let resolveState = "unresolved";
|
|
134
|
+
let sessionId = ""; // empty until resolved
|
|
135
|
+
let ownedStartEmitted = false; // we emit our own SessionStart at most once
|
|
136
|
+
let sessionGoal = null;
|
|
137
|
+
let signalCount = 0;
|
|
138
|
+
let startedAt = Date.now();
|
|
139
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
140
|
+
// ── surfil binary discovery ─────────────────────────────────────────────────────
|
|
141
|
+
function surfilBinary() {
|
|
142
|
+
// Honour explicit override, then search common install locations
|
|
143
|
+
if (process.env["SURFIL_BIN"])
|
|
144
|
+
return process.env["SURFIL_BIN"];
|
|
145
|
+
const candidates = [
|
|
146
|
+
"/usr/local/bin/surfil",
|
|
147
|
+
path.join(os.homedir(), ".local", "bin", "surfil"),
|
|
148
|
+
path.join(os.homedir(), ".surfil", "bin", "surfil"),
|
|
149
|
+
"surfil", // PATH fallback
|
|
150
|
+
];
|
|
151
|
+
for (const c of candidates) {
|
|
152
|
+
if (c === "surfil")
|
|
153
|
+
return c; // let shell resolve
|
|
154
|
+
try {
|
|
155
|
+
fs.accessSync(c, fs.constants.X_OK);
|
|
156
|
+
return c;
|
|
157
|
+
}
|
|
158
|
+
catch { /* next */ }
|
|
159
|
+
}
|
|
160
|
+
return "surfil";
|
|
161
|
+
}
|
|
162
|
+
function isSurfilInstalled() {
|
|
163
|
+
try {
|
|
164
|
+
const r = cp.spawnSync(surfilBinary(), ["version"], {
|
|
165
|
+
timeout: 3000,
|
|
166
|
+
encoding: "utf8",
|
|
167
|
+
env: { ...process.env, SURFIL_KEYSTORE: process.env["SURFIL_KEYSTORE"] ?? "file://" },
|
|
168
|
+
});
|
|
169
|
+
return r.status === 0;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// ── Signal emission via surfil emit ─────────────────────────────────────────────
|
|
176
|
+
function emitSignal(event, tool, extraArgs = []) {
|
|
177
|
+
if (!sessionId)
|
|
178
|
+
return; // never emit before the session id is resolved
|
|
179
|
+
try {
|
|
180
|
+
const args = [
|
|
181
|
+
"emit",
|
|
182
|
+
"--event", event,
|
|
183
|
+
"--session", sessionId,
|
|
184
|
+
"--tool", tool,
|
|
185
|
+
...extraArgs,
|
|
186
|
+
];
|
|
187
|
+
const r = cp.spawnSync(surfilBinary(), args, {
|
|
188
|
+
timeout: 5000,
|
|
189
|
+
encoding: "utf8",
|
|
190
|
+
env: { ...process.env, SURFIL_KEYSTORE: process.env["SURFIL_KEYSTORE"] ?? "file://" },
|
|
191
|
+
});
|
|
192
|
+
// M12: only count signals the agent actually accepted. A missing binary
|
|
193
|
+
// (r.error) or a non-zero exit means the emit did not land — don't inflate
|
|
194
|
+
// the counter.
|
|
195
|
+
if (!r.error && r.status === 0)
|
|
196
|
+
signalCount++;
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// Silent — never crash the MCP server over a signal failure
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// ── Lifecycle / resolution ────────────────────────────────────────────────────
|
|
203
|
+
function commitAttached(id, goal) {
|
|
204
|
+
sessionId = id;
|
|
205
|
+
if (sessionGoal == null && goal)
|
|
206
|
+
sessionGoal = goal;
|
|
207
|
+
resolveState = "attached";
|
|
208
|
+
process.env["SURFIL_SESSION_ID"] = sessionId;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Non-owning probe. Attaches to an identifiable session (authoritative adapter
|
|
212
|
+
* env id first, then session.json) but NEVER mints a session. Safe to call at
|
|
213
|
+
* Initialize and from surfil_status so a harmless status check never creates a
|
|
214
|
+
* row. No-op once resolved.
|
|
215
|
+
*/
|
|
216
|
+
function probeAttach() {
|
|
217
|
+
if (resolveState !== "unresolved")
|
|
218
|
+
return;
|
|
219
|
+
const envId = adapterEnvSessionId();
|
|
220
|
+
if (envId) {
|
|
221
|
+
commitAttached(envId);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const a = readActiveSession();
|
|
225
|
+
if (a && isAttachable(a))
|
|
226
|
+
commitAttached(a.sessionId, a.goal);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Owning resolve. Used right before we emit a real signal (goal/track): attach
|
|
230
|
+
* if we can identify the session, briefly polling to absorb the hook-write race
|
|
231
|
+
* and partial reads; only if nothing attachable appears do we mint our own
|
|
232
|
+
* session. Single-flight so concurrent tool calls share one decision.
|
|
233
|
+
*/
|
|
234
|
+
let owningResolve = null;
|
|
235
|
+
function resolveForEmit() {
|
|
236
|
+
if (resolveState !== "unresolved")
|
|
237
|
+
return Promise.resolve();
|
|
238
|
+
if (!owningResolve)
|
|
239
|
+
owningResolve = doOwningResolve();
|
|
240
|
+
return owningResolve;
|
|
241
|
+
}
|
|
242
|
+
async function doOwningResolve() {
|
|
243
|
+
const envId = adapterEnvSessionId();
|
|
244
|
+
if (envId) {
|
|
245
|
+
commitAttached(envId);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
for (let i = 0; i < 6; i++) { // ~6 × 180ms ≈ 1s grace for the SessionStart hook
|
|
249
|
+
const a = readActiveSession();
|
|
250
|
+
if (a && isAttachable(a)) {
|
|
251
|
+
commitAttached(a.sessionId, a.goal);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (i < 5)
|
|
255
|
+
await sleep(180);
|
|
256
|
+
}
|
|
257
|
+
// Standalone host (no hooks, no manual session): own the lifecycle ourselves.
|
|
258
|
+
sessionId = crypto.randomUUID();
|
|
259
|
+
resolveState = "owned";
|
|
260
|
+
process.env["SURFIL_SESSION_ID"] = sessionId;
|
|
261
|
+
}
|
|
262
|
+
// Emit our own SessionStart exactly once, and only for sessions we own. Attached
|
|
263
|
+
// sessions are created and finalized by the adapter's own hooks, so emitting our
|
|
264
|
+
// own lifecycle signals there would duplicate or prematurely end the session.
|
|
265
|
+
function ensureOwnedStart() {
|
|
266
|
+
if (resolveState === "owned" && !ownedStartEmitted) {
|
|
267
|
+
startedAt = Date.now();
|
|
268
|
+
emitSignal("SessionStart", TOOL_NAME);
|
|
269
|
+
ownedStartEmitted = true;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function maybeEmitEnd() {
|
|
273
|
+
if (resolveState === "owned" && ownedStartEmitted) {
|
|
274
|
+
const goalArgs = sessionGoal ? ["--goal", sessionGoal] : [];
|
|
275
|
+
emitSignal("SessionEnd", TOOL_NAME, goalArgs);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Short, display-only session label for tool responses. Shows "(pending)" until
|
|
279
|
+
// the session id is resolved on the first goal/track call.
|
|
280
|
+
function shortSessionLabel() {
|
|
281
|
+
return sessionId ? sessionId.replace(/-/g, "").slice(0, 8).toUpperCase() : "(pending)";
|
|
282
|
+
}
|
|
283
|
+
// ── MCP Server ────────────────────────────────────────────────────────────────
|
|
284
|
+
const server = new index_js_1.Server({ name: "surfil", version: "1.0.0" }, { capabilities: { tools: {}, prompts: {} } });
|
|
285
|
+
// Map MCP clientInfo.name values → valid Surfil tool names.
|
|
286
|
+
// Called at Initialize time; overrides the env-var detection for better accuracy.
|
|
287
|
+
// Covers canonical names, capitalization variants, and branded product names.
|
|
288
|
+
const CLIENT_NAME_MAP = {
|
|
289
|
+
// GitHub Copilot
|
|
290
|
+
"github-copilot": "copilot",
|
|
291
|
+
"copilot": "copilot",
|
|
292
|
+
"github-copilot-chat": "copilot",
|
|
293
|
+
// OpenCode
|
|
294
|
+
"opencode": "opencode",
|
|
295
|
+
// Cursor
|
|
296
|
+
"cursor": "cursor",
|
|
297
|
+
// VS Code / Copilot Chat in VS Code
|
|
298
|
+
"visual-studio-code": "vscode",
|
|
299
|
+
"vscode": "vscode",
|
|
300
|
+
"vs-code": "vscode",
|
|
301
|
+
// Windsurf
|
|
302
|
+
"windsurf": "windsurf",
|
|
303
|
+
"windsurf-ide": "windsurf",
|
|
304
|
+
"codeium-windsurf": "windsurf",
|
|
305
|
+
// Claude Code
|
|
306
|
+
"claude-code": "claude-code",
|
|
307
|
+
"claude": "claude-code",
|
|
308
|
+
// Cline
|
|
309
|
+
"cline": "cline",
|
|
310
|
+
// Roo Code — sends "Roo Code", "RooCode", or "roo-code"
|
|
311
|
+
"roo": "roo",
|
|
312
|
+
"roo-code": "roo",
|
|
313
|
+
"roocode": "roo",
|
|
314
|
+
"roo-codeapp": "roo",
|
|
315
|
+
// Continue.dev
|
|
316
|
+
"continue": "continue",
|
|
317
|
+
"continue-ai": "continue",
|
|
318
|
+
"continuecode": "continue",
|
|
319
|
+
"continue-dev": "continue",
|
|
320
|
+
// Aider
|
|
321
|
+
"aider": "aider",
|
|
322
|
+
"aider-chat": "aider",
|
|
323
|
+
// Codex CLI (OpenAI)
|
|
324
|
+
"codex": "codex",
|
|
325
|
+
"codex-cli": "codex",
|
|
326
|
+
"openai-codex": "codex",
|
|
327
|
+
"openai-codex-cli": "codex",
|
|
328
|
+
// Warp terminal
|
|
329
|
+
"warp": "warp",
|
|
330
|
+
"warp-terminal": "warp",
|
|
331
|
+
"warp-app": "warp",
|
|
332
|
+
// JetBrains — covers all IDEs
|
|
333
|
+
"jetbrains": "jetbrains",
|
|
334
|
+
"jetbrains-ide": "jetbrains",
|
|
335
|
+
"jetbrains-gateway": "jetbrains",
|
|
336
|
+
"intellij-idea": "jetbrains",
|
|
337
|
+
"intellij": "jetbrains",
|
|
338
|
+
"idea": "jetbrains",
|
|
339
|
+
"webstorm": "jetbrains",
|
|
340
|
+
"pycharm": "jetbrains",
|
|
341
|
+
"goland": "jetbrains",
|
|
342
|
+
"rider": "jetbrains",
|
|
343
|
+
"clion": "jetbrains",
|
|
344
|
+
"phpstorm": "jetbrains",
|
|
345
|
+
"rubymine": "jetbrains",
|
|
346
|
+
"datagrip": "jetbrains",
|
|
347
|
+
"android-studio": "jetbrains",
|
|
348
|
+
// Zed
|
|
349
|
+
"zed": "zed",
|
|
350
|
+
"zed-editor": "zed",
|
|
351
|
+
// Gemini CLI (Google)
|
|
352
|
+
"gemini-cli": "gemini-cli",
|
|
353
|
+
"gemini": "gemini-cli",
|
|
354
|
+
"google-gemini": "gemini-cli",
|
|
355
|
+
"google-gemini-cli": "gemini-cli",
|
|
356
|
+
"gemini-code": "gemini-cli",
|
|
357
|
+
// Neovim
|
|
358
|
+
"neovim": "neovim",
|
|
359
|
+
"nvim": "neovim",
|
|
360
|
+
// PowerShell
|
|
361
|
+
"powershell": "powershell",
|
|
362
|
+
"pwsh": "powershell",
|
|
363
|
+
// Generic CLI
|
|
364
|
+
"cli": "cli",
|
|
365
|
+
};
|
|
366
|
+
// Detect which AI tool is calling us from env hints.
|
|
367
|
+
// Check both purpose-set MCP flags and well-known CLI env vars.
|
|
368
|
+
function detectTool() {
|
|
369
|
+
// Explicit adapter override always wins
|
|
370
|
+
if (process.env["SURFIL_ADAPTER"])
|
|
371
|
+
return process.env["SURFIL_ADAPTER"];
|
|
372
|
+
// MCP-specific flags
|
|
373
|
+
if (process.env["COPILOT_MCP"])
|
|
374
|
+
return "copilot";
|
|
375
|
+
if (process.env["CURSOR_MCP"])
|
|
376
|
+
return "cursor";
|
|
377
|
+
if (process.env["VSCODE_MCP"])
|
|
378
|
+
return "vscode";
|
|
379
|
+
if (process.env["WINDSURF_MCP"])
|
|
380
|
+
return "windsurf";
|
|
381
|
+
// GitHub Copilot CLI — sets COPILOT_CLI=1, COPILOT_RUN_APP=1, COPILOT_AGENT_SESSION_ID
|
|
382
|
+
if (process.env["COPILOT_CLI"] || process.env["COPILOT_RUN_APP"] || process.env["COPILOT_AGENT_SESSION_ID"])
|
|
383
|
+
return "copilot";
|
|
384
|
+
// Cursor desktop sets CURSOR_TRACE_ID
|
|
385
|
+
if (process.env["CURSOR_TRACE_ID"])
|
|
386
|
+
return "cursor";
|
|
387
|
+
// VS Code sets VSCODE_PID
|
|
388
|
+
if (process.env["VSCODE_PID"])
|
|
389
|
+
return "vscode";
|
|
390
|
+
// Gemini CLI sets GEMINI_CLI or GOOGLE_CLOUD_CLI_*
|
|
391
|
+
if (process.env["GEMINI_CLI"] || process.env["GEMINI_API_KEY_SOURCE"] === "gemini-cli")
|
|
392
|
+
return "gemini-cli";
|
|
393
|
+
// Codex CLI (OpenAI) sets CODEX_CLI or OPENAI_CODEX_CLI
|
|
394
|
+
if (process.env["CODEX_CLI"] || process.env["OPENAI_CODEX_CLI"])
|
|
395
|
+
return "codex";
|
|
396
|
+
// Warp terminal sets WARP_IS_LOCAL_SHELL_SESSION or TERM_PROGRAM=WarpTerminal
|
|
397
|
+
if (process.env["WARP_IS_LOCAL_SHELL_SESSION"] || process.env["TERM_PROGRAM"] === "WarpTerminal")
|
|
398
|
+
return "warp";
|
|
399
|
+
return "unknown";
|
|
400
|
+
}
|
|
401
|
+
// Map a raw MCP clientInfo.name to a valid Surfil tool name.
|
|
402
|
+
// Falls back to the env-detected TOOL_NAME if no mapping exists.
|
|
403
|
+
function clientNameToTool(clientName, fallback) {
|
|
404
|
+
if (!clientName)
|
|
405
|
+
return fallback;
|
|
406
|
+
const normalized = clientName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
407
|
+
return CLIENT_NAME_MAP[normalized] ?? fallback;
|
|
408
|
+
}
|
|
409
|
+
// Mutable — updated at Initialize time from clientInfo.name for better accuracy.
|
|
410
|
+
let TOOL_NAME = detectTool();
|
|
411
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
412
|
+
tools: [
|
|
413
|
+
{
|
|
414
|
+
name: "surfil_set_goal",
|
|
415
|
+
description: "Set the goal or task description for the current Surfil session. Call this at the start of a task to get accurate session tracking.",
|
|
416
|
+
inputSchema: {
|
|
417
|
+
type: "object",
|
|
418
|
+
properties: {
|
|
419
|
+
goal: {
|
|
420
|
+
type: "string",
|
|
421
|
+
description: "The task or goal for this AI coding session (e.g. 'Fix the login bug in auth.ts')",
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
required: ["goal"],
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
name: "surfil_status",
|
|
429
|
+
description: "Get the current Surfil session status — session ID, goal, duration, and signal count.",
|
|
430
|
+
inputSchema: {
|
|
431
|
+
type: "object",
|
|
432
|
+
properties: {},
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: "surfil_track",
|
|
437
|
+
description: "Track a completed tool use or task step in the current Surfil session. Call after completing a meaningful action.",
|
|
438
|
+
inputSchema: {
|
|
439
|
+
type: "object",
|
|
440
|
+
properties: {
|
|
441
|
+
tool: {
|
|
442
|
+
type: "string",
|
|
443
|
+
description: "Name of the tool or action that was used",
|
|
444
|
+
},
|
|
445
|
+
success: {
|
|
446
|
+
type: "boolean",
|
|
447
|
+
description: "Whether the tool use succeeded",
|
|
448
|
+
default: true,
|
|
449
|
+
},
|
|
450
|
+
model: {
|
|
451
|
+
type: "string",
|
|
452
|
+
description: "Optional model name used for this step (e.g. gpt-4o, claude-3-5-sonnet)",
|
|
453
|
+
},
|
|
454
|
+
tokens_used: {
|
|
455
|
+
type: "integer",
|
|
456
|
+
description: "Optional token count for this step",
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
required: ["tool"],
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
name: "surfil_telemetry",
|
|
464
|
+
description: "Report model usage and cost telemetry for the current Surfil session. Call after each LLM turn to surface tokens, model, and cost in the dashboard.",
|
|
465
|
+
inputSchema: {
|
|
466
|
+
type: "object",
|
|
467
|
+
properties: {
|
|
468
|
+
model: {
|
|
469
|
+
type: "string",
|
|
470
|
+
description: "Model name (e.g. gpt-4o, claude-3-5-sonnet-20241022)",
|
|
471
|
+
},
|
|
472
|
+
tokens_used: {
|
|
473
|
+
type: "integer",
|
|
474
|
+
description: "Total tokens consumed (prompt + completion)",
|
|
475
|
+
},
|
|
476
|
+
tokens_saved: {
|
|
477
|
+
type: "integer",
|
|
478
|
+
description: "Tokens saved by Surfil optimizations (if known)",
|
|
479
|
+
default: 0,
|
|
480
|
+
},
|
|
481
|
+
cost_usd: {
|
|
482
|
+
type: "number",
|
|
483
|
+
description: "Estimated cost in USD for this turn",
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
required: ["model", "tokens_used"],
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
name: "temper_instructions",
|
|
491
|
+
description: `Return the Surfil Temper Standard behavioral governance rules for AI coding assistants. Exposes the same content as the 'temper' prompt but as a callable tool. Use when you need to programmatically retrieve the rules at a specific enforcement level.`,
|
|
492
|
+
inputSchema: {
|
|
493
|
+
type: "object",
|
|
494
|
+
properties: {
|
|
495
|
+
mode: {
|
|
496
|
+
type: "string",
|
|
497
|
+
enum: ["basic", "full", "ultra"],
|
|
498
|
+
description: "Enforcement level. 'basic' = advisory; 'full' (default) = enforced; 'ultra' = extremist + scope challenges.",
|
|
499
|
+
default: "full",
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
// MCP annotation: read-only, no side effects.
|
|
504
|
+
annotations: {
|
|
505
|
+
readOnlyHint: true,
|
|
506
|
+
openWorldHint: false,
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
name: "surfil_retrieve",
|
|
511
|
+
description: `Retrieve the byte-exact original content for a compressed CCR entry by its content hash. Use this when the model needs the full uncompressed version of a compressed context block. Read-only — no side effects. Max 3 retrievals per hash (DR-14).`,
|
|
512
|
+
inputSchema: {
|
|
513
|
+
type: "object",
|
|
514
|
+
properties: {
|
|
515
|
+
hash: {
|
|
516
|
+
type: "string",
|
|
517
|
+
description: "The SHA-256 content hash of the CCR entry to retrieve.",
|
|
518
|
+
},
|
|
519
|
+
query: {
|
|
520
|
+
type: "string",
|
|
521
|
+
description: "Optional search query within the retrieved content (for BM25 search).",
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
required: ["hash"],
|
|
525
|
+
},
|
|
526
|
+
annotations: {
|
|
527
|
+
readOnlyHint: true,
|
|
528
|
+
openWorldHint: false,
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
],
|
|
532
|
+
}));
|
|
533
|
+
// ── Prompts — temper ──────────────────────────────────────────────────────────
|
|
534
|
+
// The `temper` prompt exposes the Surfil Temper Standard as an MCP prompt entry.
|
|
535
|
+
// Hosts that only have access to the prompt menu (not tool calls) can inject
|
|
536
|
+
// the rules directly by selecting this prompt.
|
|
537
|
+
server.setRequestHandler(types_js_1.ListPromptsRequestSchema, async () => ({
|
|
538
|
+
prompts: [
|
|
539
|
+
{
|
|
540
|
+
name: "temper",
|
|
541
|
+
description: `Surfil Temper Standard v${temper_standard_js_1.TEMPER_VERSION} — behavioral governance rules for AI coding assistants. Enforces the Restraint Ladder, Token-Efficiency Rules, and Non-Negotiable Carve-Outs.`,
|
|
542
|
+
arguments: [
|
|
543
|
+
{
|
|
544
|
+
name: "mode",
|
|
545
|
+
description: "Enforcement level: 'basic' (advisory), 'full' (default, enforced), or 'ultra' (extremist + scope challenges).",
|
|
546
|
+
required: false,
|
|
547
|
+
},
|
|
548
|
+
],
|
|
549
|
+
},
|
|
550
|
+
],
|
|
551
|
+
}));
|
|
552
|
+
server.setRequestHandler(types_js_1.GetPromptRequestSchema, async (req) => {
|
|
553
|
+
const { name, arguments: promptArgs } = req.params;
|
|
554
|
+
if (name !== "temper") {
|
|
555
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
556
|
+
}
|
|
557
|
+
const mode = String(promptArgs?.mode ?? "full");
|
|
558
|
+
const text = (0, temper_standard_js_1.getStandardForLevel)(mode);
|
|
559
|
+
return {
|
|
560
|
+
description: `Surfil Temper Standard v${temper_standard_js_1.TEMPER_VERSION} — level: ${mode}`,
|
|
561
|
+
messages: [
|
|
562
|
+
{
|
|
563
|
+
role: "user",
|
|
564
|
+
content: {
|
|
565
|
+
type: "text",
|
|
566
|
+
text,
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
};
|
|
571
|
+
});
|
|
572
|
+
async function handleCallTool(req) {
|
|
573
|
+
const { name, arguments: args } = req.params;
|
|
574
|
+
if (name === "surfil_set_goal") {
|
|
575
|
+
const goal = String(args?.goal ?? "").slice(0, 500);
|
|
576
|
+
sessionGoal = goal;
|
|
577
|
+
await resolveForEmit();
|
|
578
|
+
ensureOwnedStart();
|
|
579
|
+
// UserPromptSubmit carries goal_declared into the resolved session so the
|
|
580
|
+
// engine records it (works whether attached to the adapter session or owned).
|
|
581
|
+
emitSignal("UserPromptSubmit", TOOL_NAME, ["--goal", goal]);
|
|
582
|
+
return {
|
|
583
|
+
content: [{ type: "text", text: `✅ Surfil goal set: "${goal}" (session ${shortSessionLabel()})` }],
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
if (name === "surfil_status") {
|
|
587
|
+
probeAttach(); // identify the session if possible, but never create one
|
|
588
|
+
const installed = isSurfilInstalled();
|
|
589
|
+
const durationSec = Math.floor((Date.now() - startedAt) / 1000);
|
|
590
|
+
const m = Math.floor(durationSec / 60);
|
|
591
|
+
const s = durationSec % 60;
|
|
592
|
+
const mode = resolveState === "attached"
|
|
593
|
+
? "attached (adapter/surfil-on session)"
|
|
594
|
+
: resolveState === "owned"
|
|
595
|
+
? "standalone (surfil-mcp owns this session)"
|
|
596
|
+
: "pending (starts on first goal/track)";
|
|
597
|
+
return {
|
|
598
|
+
content: [{
|
|
599
|
+
type: "text",
|
|
600
|
+
text: [
|
|
601
|
+
`**Surfil Session**`,
|
|
602
|
+
`ID: ${shortSessionLabel()}`,
|
|
603
|
+
`Adapter: ${TOOL_NAME}`,
|
|
604
|
+
`Mode: ${mode}`,
|
|
605
|
+
`Duration: ${m > 0 ? `${m}m ` : ""}${s}s`,
|
|
606
|
+
`Signals sent: ${signalCount}`,
|
|
607
|
+
`Goal: ${sessionGoal ?? "(not set — call surfil_set_goal)"}`,
|
|
608
|
+
`Agent binary: ${installed ? "✅ found" : "❌ not found — install from surfil.com"}`,
|
|
609
|
+
].join("\n"),
|
|
610
|
+
}],
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
if (name === "surfil_track") {
|
|
614
|
+
const tool = String(args?.tool ?? "unknown");
|
|
615
|
+
const success = args?.success !== false;
|
|
616
|
+
const model = String(args?.model ?? "");
|
|
617
|
+
const tokensUsed = Number(args?.tokens_used ?? 0);
|
|
618
|
+
await resolveForEmit();
|
|
619
|
+
ensureOwnedStart();
|
|
620
|
+
const extra = ["--tool", tool, "--success", success ? "true" : "false"];
|
|
621
|
+
if (model)
|
|
622
|
+
extra.push("--model", model);
|
|
623
|
+
if (tokensUsed > 0)
|
|
624
|
+
extra.push("--tokens-used", String(tokensUsed));
|
|
625
|
+
emitSignal("PostToolUse", TOOL_NAME, extra);
|
|
626
|
+
return {
|
|
627
|
+
content: [{ type: "text", text: `✅ Tracked: ${tool} (success=${success})` }],
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
if (name === "surfil_telemetry") {
|
|
631
|
+
const model = String(args?.model ?? "");
|
|
632
|
+
const tokensUsed = Number(args?.tokens_used ?? 0);
|
|
633
|
+
const tokensSaved = Number(args?.tokens_saved ?? 0);
|
|
634
|
+
const costUsd = Number(args?.cost_usd ?? 0);
|
|
635
|
+
await resolveForEmit();
|
|
636
|
+
ensureOwnedStart();
|
|
637
|
+
const extra = [];
|
|
638
|
+
if (model)
|
|
639
|
+
extra.push("--model", model);
|
|
640
|
+
if (tokensUsed > 0)
|
|
641
|
+
extra.push("--tokens-used", String(tokensUsed));
|
|
642
|
+
if (tokensSaved > 0)
|
|
643
|
+
extra.push("--tokens-saved", String(tokensSaved));
|
|
644
|
+
if (costUsd > 0)
|
|
645
|
+
extra.push("--cost-usd", String(costUsd));
|
|
646
|
+
// Emit as a synthetic PostToolUse with telemetry payload so the engine
|
|
647
|
+
// records model and tokens for dashboard surfacing.
|
|
648
|
+
emitSignal("PostToolUse", TOOL_NAME, extra);
|
|
649
|
+
return {
|
|
650
|
+
content: [{ type: "text", text: `📊 Telemetry: ${model} · ${tokensUsed.toLocaleString()} tokens` }],
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
if (name === "temper_instructions") {
|
|
654
|
+
const mode = String(args?.mode ?? "full");
|
|
655
|
+
const text = (0, temper_standard_js_1.getStandardForLevel)(mode);
|
|
656
|
+
return {
|
|
657
|
+
content: [{ type: "text", text }],
|
|
658
|
+
// No side effects: read-only return of the embedded standard.
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
if (name === "surfil_retrieve") {
|
|
662
|
+
const hash = String(args?.hash ?? "");
|
|
663
|
+
if (!hash) {
|
|
664
|
+
return { content: [{ type: "text", text: "Error: hash is required" }], isError: true };
|
|
665
|
+
}
|
|
666
|
+
// In production, this calls the engine CCR store via retrieveFromEngine.
|
|
667
|
+
// For the framework, we return a placeholder indicating the retrieval mechanism.
|
|
668
|
+
// The actual retrieval is wired when the engine endpoint is deployed.
|
|
669
|
+
const query = args?.query ? ` (query: "${String(args.query)}")` : "";
|
|
670
|
+
return {
|
|
671
|
+
content: [{
|
|
672
|
+
type: "text",
|
|
673
|
+
text: `Retrieve request for hash ${hash}${query} — use engine /temper/ccr-retrieve endpoint. Max 3 rounds per hash (DR-14). Read-only: no content was modified.`,
|
|
674
|
+
}],
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
return {
|
|
678
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
679
|
+
isError: true,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, handleCallTool);
|
|
683
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
684
|
+
async function main() {
|
|
685
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
686
|
+
// Resolve the session id from clientInfo first so source-matching is correct,
|
|
687
|
+
// then run a NON-OWNING probe: for Copilot this attaches to the authoritative
|
|
688
|
+
// COPILOT_AGENT_SESSION_ID immediately (race-free); otherwise it stays
|
|
689
|
+
// unresolved until the first goal/track call, which avoids the load-time race
|
|
690
|
+
// that previously minted a duplicate session.
|
|
691
|
+
server.setRequestHandler(types_js_1.InitializeRequestSchema, async (req) => {
|
|
692
|
+
const rawClientName = req.params?.clientInfo?.name ?? "";
|
|
693
|
+
TOOL_NAME = clientNameToTool(rawClientName, TOOL_NAME);
|
|
694
|
+
probeAttach();
|
|
695
|
+
// Standalone host (VS Code, Cursor, etc.) — no adapter hooks and no manual
|
|
696
|
+
// session. Immediately own the lifecycle so a session row exists from turn 1.
|
|
697
|
+
if (resolveState === "unresolved") {
|
|
698
|
+
sessionId = crypto.randomUUID();
|
|
699
|
+
resolveState = "owned";
|
|
700
|
+
process.env["SURFIL_SESSION_ID"] = sessionId;
|
|
701
|
+
ensureOwnedStart();
|
|
702
|
+
}
|
|
703
|
+
return {
|
|
704
|
+
protocolVersion: "2024-11-05",
|
|
705
|
+
capabilities: { tools: {} },
|
|
706
|
+
serverInfo: { name: "surfil", version: "1.0.0" },
|
|
707
|
+
};
|
|
708
|
+
});
|
|
709
|
+
// On exit, finalize only sessions we own; attached adapter sessions are
|
|
710
|
+
// finalized by the adapter's own SessionEnd hook.
|
|
711
|
+
const shutdown = () => {
|
|
712
|
+
maybeEmitEnd();
|
|
713
|
+
process.exit(0);
|
|
714
|
+
};
|
|
715
|
+
process.on("SIGTERM", shutdown);
|
|
716
|
+
process.on("SIGINT", shutdown);
|
|
717
|
+
process.on("disconnect", shutdown);
|
|
718
|
+
// Heartbeat: keep owned-session last_signal_at fresh so the dashboard marks
|
|
719
|
+
// it live. Attached sessions rely on adapter hooks; owned sessions beat here.
|
|
720
|
+
setInterval(() => {
|
|
721
|
+
if (resolveState === "owned" && ownedStartEmitted) {
|
|
722
|
+
emitSignal("Heartbeat", TOOL_NAME);
|
|
723
|
+
}
|
|
724
|
+
}, 30000);
|
|
725
|
+
await server.connect(transport);
|
|
726
|
+
}
|
|
727
|
+
// Entrypoint guard: only start the stdio server when executed directly
|
|
728
|
+
// (`node dist/index.js`). When imported by the test suite, the handlers and
|
|
729
|
+
// emitSignal are exercised in isolation without spawning a server.
|
|
730
|
+
if (typeof require !== "undefined" && require.main === module) {
|
|
731
|
+
main().catch((err) => {
|
|
732
|
+
process.stderr.write(`surfil-mcp error: ${err}\n`);
|
|
733
|
+
process.exit(1);
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
// Test-only surface: lets the suite drive the REAL emitSignal (and observe the
|
|
737
|
+
// signal counter) instead of re-implementing the logic. Not part of the public
|
|
738
|
+
// MCP contract.
|
|
739
|
+
exports.__test = {
|
|
740
|
+
emitSignal,
|
|
741
|
+
signalCount: () => signalCount,
|
|
742
|
+
reset(id) {
|
|
743
|
+
sessionId = id;
|
|
744
|
+
signalCount = 0;
|
|
745
|
+
},
|
|
746
|
+
handleCallTool,
|
|
747
|
+
getGoal: () => sessionGoal,
|
|
748
|
+
};
|
|
749
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* retrieve.ts — Task 9.6: Retrieve-on-demand via surfil-mcp (family J) + multi-turn tracker.
|
|
3
|
+
*
|
|
4
|
+
* Exposes `surfil_retrieve(hash, query?)` as a Surfil-MCP tool. The tool calls the
|
|
5
|
+
* engine's CCR store (9.2) to retrieve the byte-exact original content by hash.
|
|
6
|
+
*
|
|
7
|
+
* Multi-turn tracker: tracks which compressed entries have been referenced across
|
|
8
|
+
* turns and proactively re-expands them when a later turn references the same hash.
|
|
9
|
+
*
|
|
10
|
+
* DR-14: max 3 retrieve rounds per hash.
|
|
11
|
+
* Mode: OO (overlay/observability), read-only tool.
|
|
12
|
+
* Ownership-scoped: retrieval is scoped to the current session/user.
|
|
13
|
+
*/
|
|
14
|
+
export interface RetrieveRecord {
|
|
15
|
+
hash: string;
|
|
16
|
+
retrievedAt: number;
|
|
17
|
+
turn: number;
|
|
18
|
+
roundsUsed: number;
|
|
19
|
+
}
|
|
20
|
+
export interface MultiTurnTracker {
|
|
21
|
+
records: Map<string, RetrieveRecord>;
|
|
22
|
+
currentTurn: number;
|
|
23
|
+
totalRetrievals: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create a new multi-turn tracker.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createTracker(): MultiTurnTracker;
|
|
29
|
+
/**
|
|
30
|
+
* advanceTurn increments the turn counter.
|
|
31
|
+
* Called at the start of each new model turn.
|
|
32
|
+
*/
|
|
33
|
+
export declare function advanceTurn(tracker: MultiTurnTracker): void;
|
|
34
|
+
/**
|
|
35
|
+
* recordRetrieve logs a retrieve action in the multi-turn tracker.
|
|
36
|
+
* Returns false if the max rounds (DR-14: 3) have been exceeded for this hash.
|
|
37
|
+
*/
|
|
38
|
+
export declare function recordRetrieve(tracker: MultiTurnTracker, hash: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* getReferencedHashes returns all hashes that have been referenced across turns.
|
|
41
|
+
* Used for proactive re-expansion.
|
|
42
|
+
*/
|
|
43
|
+
export declare function getReferencedHashes(tracker: MultiTurnTracker): string[];
|
|
44
|
+
/**
|
|
45
|
+
* isMaxRoundsReached checks if a hash has exhausted its retrieve rounds.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isMaxRoundsReached(tracker: MultiTurnTracker, hash: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* getRetrieveCount returns how many times a hash has been retrieved.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getRetrieveCount(tracker: MultiTurnTracker, hash: string): number;
|
|
52
|
+
/**
|
|
53
|
+
* clearTracker resets the multi-turn tracker (e.g. on session end).
|
|
54
|
+
*/
|
|
55
|
+
export declare function clearTracker(tracker: MultiTurnTracker): void;
|
|
56
|
+
export interface RetrieveResult {
|
|
57
|
+
found: boolean;
|
|
58
|
+
original: string | null;
|
|
59
|
+
hash: string;
|
|
60
|
+
roundsUsed: number;
|
|
61
|
+
maxRoundsReached: boolean;
|
|
62
|
+
error: string | null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* retrieveFromEngine calls the engine's CCR store endpoint to retrieve content by hash.
|
|
66
|
+
* In production, this makes an HTTP call to engine.surfil.com/temper/ccr-retrieve?hash=...
|
|
67
|
+
* For the framework, this is a placeholder that the MCP server wires up.
|
|
68
|
+
*/
|
|
69
|
+
export declare function retrieveFromEngine(hash: string, engineUrl: string, internalKey: string, tracker: MultiTurnTracker): Promise<RetrieveResult>;
|
|
70
|
+
//# sourceMappingURL=retrieve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retrieve.d.ts","sourceRoot":"","sources":["../src/retrieve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,gBAAgB,CAMhD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAE3D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAiB/E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAEvE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGnF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAI5D;AAID,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,cAAc,CAAC,CA0DzB"}
|
package/dist/retrieve.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* retrieve.ts — Task 9.6: Retrieve-on-demand via surfil-mcp (family J) + multi-turn tracker.
|
|
4
|
+
*
|
|
5
|
+
* Exposes `surfil_retrieve(hash, query?)` as a Surfil-MCP tool. The tool calls the
|
|
6
|
+
* engine's CCR store (9.2) to retrieve the byte-exact original content by hash.
|
|
7
|
+
*
|
|
8
|
+
* Multi-turn tracker: tracks which compressed entries have been referenced across
|
|
9
|
+
* turns and proactively re-expands them when a later turn references the same hash.
|
|
10
|
+
*
|
|
11
|
+
* DR-14: max 3 retrieve rounds per hash.
|
|
12
|
+
* Mode: OO (overlay/observability), read-only tool.
|
|
13
|
+
* Ownership-scoped: retrieval is scoped to the current session/user.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.createTracker = createTracker;
|
|
17
|
+
exports.advanceTurn = advanceTurn;
|
|
18
|
+
exports.recordRetrieve = recordRetrieve;
|
|
19
|
+
exports.getReferencedHashes = getReferencedHashes;
|
|
20
|
+
exports.isMaxRoundsReached = isMaxRoundsReached;
|
|
21
|
+
exports.getRetrieveCount = getRetrieveCount;
|
|
22
|
+
exports.clearTracker = clearTracker;
|
|
23
|
+
exports.retrieveFromEngine = retrieveFromEngine;
|
|
24
|
+
/**
|
|
25
|
+
* Create a new multi-turn tracker.
|
|
26
|
+
*/
|
|
27
|
+
function createTracker() {
|
|
28
|
+
return {
|
|
29
|
+
records: new Map(),
|
|
30
|
+
currentTurn: 0,
|
|
31
|
+
totalRetrievals: 0,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* advanceTurn increments the turn counter.
|
|
36
|
+
* Called at the start of each new model turn.
|
|
37
|
+
*/
|
|
38
|
+
function advanceTurn(tracker) {
|
|
39
|
+
tracker.currentTurn++;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* recordRetrieve logs a retrieve action in the multi-turn tracker.
|
|
43
|
+
* Returns false if the max rounds (DR-14: 3) have been exceeded for this hash.
|
|
44
|
+
*/
|
|
45
|
+
function recordRetrieve(tracker, hash) {
|
|
46
|
+
const existing = tracker.records.get(hash);
|
|
47
|
+
const roundsUsed = existing ? existing.roundsUsed + 1 : 1;
|
|
48
|
+
// DR-14: max 3 retrieve rounds per hash.
|
|
49
|
+
if (roundsUsed > 3) {
|
|
50
|
+
return false; // exceeded max rounds
|
|
51
|
+
}
|
|
52
|
+
tracker.records.set(hash, {
|
|
53
|
+
hash,
|
|
54
|
+
retrievedAt: Date.now(),
|
|
55
|
+
turn: tracker.currentTurn,
|
|
56
|
+
roundsUsed,
|
|
57
|
+
});
|
|
58
|
+
tracker.totalRetrievals++;
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* getReferencedHashes returns all hashes that have been referenced across turns.
|
|
63
|
+
* Used for proactive re-expansion.
|
|
64
|
+
*/
|
|
65
|
+
function getReferencedHashes(tracker) {
|
|
66
|
+
return Array.from(tracker.records.keys());
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* isMaxRoundsReached checks if a hash has exhausted its retrieve rounds.
|
|
70
|
+
*/
|
|
71
|
+
function isMaxRoundsReached(tracker, hash) {
|
|
72
|
+
const record = tracker.records.get(hash);
|
|
73
|
+
return record !== undefined && record.roundsUsed >= 3;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* getRetrieveCount returns how many times a hash has been retrieved.
|
|
77
|
+
*/
|
|
78
|
+
function getRetrieveCount(tracker, hash) {
|
|
79
|
+
return tracker.records.get(hash)?.roundsUsed ?? 0;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* clearTracker resets the multi-turn tracker (e.g. on session end).
|
|
83
|
+
*/
|
|
84
|
+
function clearTracker(tracker) {
|
|
85
|
+
tracker.records.clear();
|
|
86
|
+
tracker.currentTurn = 0;
|
|
87
|
+
tracker.totalRetrievals = 0;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* retrieveFromEngine calls the engine's CCR store endpoint to retrieve content by hash.
|
|
91
|
+
* In production, this makes an HTTP call to engine.surfil.com/temper/ccr-retrieve?hash=...
|
|
92
|
+
* For the framework, this is a placeholder that the MCP server wires up.
|
|
93
|
+
*/
|
|
94
|
+
async function retrieveFromEngine(hash, engineUrl, internalKey, tracker) {
|
|
95
|
+
// Check if max rounds already reached (DR-14).
|
|
96
|
+
if (isMaxRoundsReached(tracker, hash)) {
|
|
97
|
+
return {
|
|
98
|
+
found: false,
|
|
99
|
+
original: null,
|
|
100
|
+
hash,
|
|
101
|
+
roundsUsed: getRetrieveCount(tracker, hash),
|
|
102
|
+
maxRoundsReached: true,
|
|
103
|
+
error: "Max retrieve rounds (3) reached for this hash — content may have expired",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const res = await fetch(`${engineUrl}/temper/ccr-retrieve?hash=${encodeURIComponent(hash)}`, {
|
|
108
|
+
method: "GET",
|
|
109
|
+
headers: { Authorization: `Bearer ${internalKey}` },
|
|
110
|
+
signal: AbortSignal.timeout(5000),
|
|
111
|
+
});
|
|
112
|
+
if (!res.ok) {
|
|
113
|
+
// Record the attempt even on failure.
|
|
114
|
+
recordRetrieve(tracker, hash);
|
|
115
|
+
return {
|
|
116
|
+
found: false,
|
|
117
|
+
original: null,
|
|
118
|
+
hash,
|
|
119
|
+
roundsUsed: getRetrieveCount(tracker, hash),
|
|
120
|
+
maxRoundsReached: false,
|
|
121
|
+
error: `Engine returned HTTP ${res.status}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const data = await res.json();
|
|
125
|
+
recordRetrieve(tracker, hash);
|
|
126
|
+
return {
|
|
127
|
+
found: data.found,
|
|
128
|
+
original: data.found ? data.original : null,
|
|
129
|
+
hash,
|
|
130
|
+
roundsUsed: getRetrieveCount(tracker, hash),
|
|
131
|
+
maxRoundsReached: isMaxRoundsReached(tracker, hash),
|
|
132
|
+
error: data.expired ? "Content expired (TTL 5min passed)" : null,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
recordRetrieve(tracker, hash);
|
|
137
|
+
return {
|
|
138
|
+
found: false,
|
|
139
|
+
original: null,
|
|
140
|
+
hash,
|
|
141
|
+
roundsUsed: getRetrieveCount(tracker, hash),
|
|
142
|
+
maxRoundsReached: false,
|
|
143
|
+
error: e instanceof Error ? e.message : String(e),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=retrieve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retrieve.js","sourceRoot":"","sources":["../src/retrieve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* temper-standard.ts
|
|
3
|
+
*
|
|
4
|
+
* TypeScript embedding of the Surfil Temper Standard for surfil-mcp.
|
|
5
|
+
* Mirrors agent/internal/temper/standard.md — update both when the standard changes.
|
|
6
|
+
* Version: 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
declare const VALID_LEVELS: readonly ["basic", "full", "ultra"];
|
|
9
|
+
/**
|
|
10
|
+
* Returns the Temper Standard text for the given enforcement level.
|
|
11
|
+
* Invalid levels fall back to "full".
|
|
12
|
+
* All levels include the Non-Negotiable Carve-Outs (never stripped).
|
|
13
|
+
*/
|
|
14
|
+
export declare function getStandardForLevel(level: string): string;
|
|
15
|
+
export declare const TEMPER_VERSION = "1.0.0";
|
|
16
|
+
export { VALID_LEVELS };
|
|
17
|
+
//# sourceMappingURL=temper-standard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temper-standard.d.ts","sourceRoot":"","sources":["../src/temper-standard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkHH,QAAA,MAAM,YAAY,qCAAsC,CAAC;AAGzD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED,eAAO,MAAM,cAAc,UAAU,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* temper-standard.ts
|
|
4
|
+
*
|
|
5
|
+
* TypeScript embedding of the Surfil Temper Standard for surfil-mcp.
|
|
6
|
+
* Mirrors agent/internal/temper/standard.md — update both when the standard changes.
|
|
7
|
+
* Version: 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.VALID_LEVELS = exports.TEMPER_VERSION = void 0;
|
|
11
|
+
exports.getStandardForLevel = getStandardForLevel;
|
|
12
|
+
const STANDARD_FULL = `# Surfil Temper Standard
|
|
13
|
+
|
|
14
|
+
Version: 1.0.0
|
|
15
|
+
Owner: Surfil (surfil.com)
|
|
16
|
+
|
|
17
|
+
The Temper Standard is a behavioral governance specification for AI coding
|
|
18
|
+
assistants operating under Surfil oversight. It encodes the principle that the
|
|
19
|
+
best code is the code that does not need to exist.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## The Restraint Ladder
|
|
24
|
+
|
|
25
|
+
Before writing any code, climb the ladder from the top. Stop at the first
|
|
26
|
+
rung that solves the problem. Never skip down to a lower rung without
|
|
27
|
+
exhausting every higher one.
|
|
28
|
+
|
|
29
|
+
1. **Does this need to exist?** If the requirement is speculative, deferred,
|
|
30
|
+
or already handled elsewhere, skip it entirely and say so in one line.
|
|
31
|
+
Building something nobody asked for is the most expensive mistake. (YAGNI)
|
|
32
|
+
|
|
33
|
+
2. **Does the standard library solve it?** Use the language's built-in
|
|
34
|
+
facilities. No dependency beats no dependency.
|
|
35
|
+
|
|
36
|
+
3. **Does a native platform feature cover it?** CSS instead of JavaScript,
|
|
37
|
+
a database constraint instead of application code, an HTTP header instead
|
|
38
|
+
of middleware, a filesystem operation instead of a library wrapper.
|
|
39
|
+
|
|
40
|
+
4. **Does an already-installed dependency solve it?** Search the existing
|
|
41
|
+
dependency manifest before adding anything. Adding a package to do what
|
|
42
|
+
an installed package already does is waste.
|
|
43
|
+
|
|
44
|
+
5. **Can it be one function or one expression?** If the entire solution fits
|
|
45
|
+
in a single readable line or a named one-liner, write that and stop.
|
|
46
|
+
|
|
47
|
+
6. **Write the minimum code that works.** No abstraction without at least two
|
|
48
|
+
callers. No interface with one implementation. No factory for one product.
|
|
49
|
+
No configuration for a value that never changes. No scaffolding "for later."
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Non-Negotiable Carve-Outs
|
|
54
|
+
|
|
55
|
+
The following are NEVER subject to the Restraint Ladder. They must be
|
|
56
|
+
implemented completely and correctly regardless of effort.
|
|
57
|
+
|
|
58
|
+
### Input validation at trust boundaries
|
|
59
|
+
Every surface that accepts external input must validate and sanitize before use.
|
|
60
|
+
|
|
61
|
+
### Error handling that prevents data loss
|
|
62
|
+
Any code path that writes, transforms, or deletes persistent data must handle
|
|
63
|
+
errors explicitly. Swallowing errors on mutation paths is not acceptable.
|
|
64
|
+
|
|
65
|
+
### Security controls
|
|
66
|
+
Authentication, authorization, cryptographic operations, secrets handling, and
|
|
67
|
+
audit logging must be implemented to their full specification.
|
|
68
|
+
|
|
69
|
+
### Accessibility basics
|
|
70
|
+
Interactive UI components must be keyboard-navigable and carry correct ARIA
|
|
71
|
+
roles and labels.
|
|
72
|
+
|
|
73
|
+
### Anything explicitly requested
|
|
74
|
+
If the user or task specification asks for something, build it in full. The
|
|
75
|
+
Restraint Ladder applies to things not asked for.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## The \`surfil-temper:\` Marker Convention
|
|
80
|
+
|
|
81
|
+
Mark deliberate simplifications with a \`surfil-temper:\` comment naming the
|
|
82
|
+
ceiling and upgrade path:
|
|
83
|
+
|
|
84
|
+
\`\`\`
|
|
85
|
+
// surfil-temper: <what was simplified> · ceiling: <known limit> · upgrade: <when/how>
|
|
86
|
+
\`\`\`
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Token-Efficiency Rules
|
|
91
|
+
|
|
92
|
+
- Keep functions, modules, and files small.
|
|
93
|
+
- Name things precisely; a precise name eliminates the need for a comment.
|
|
94
|
+
- No speculative comments.
|
|
95
|
+
- Flat over nested.
|
|
96
|
+
- One thing per unit.
|
|
97
|
+
- Delete before adding.
|
|
98
|
+
- Mark deliberate shortcuts with \`surfil-temper:\`.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Levels
|
|
103
|
+
|
|
104
|
+
### basic
|
|
105
|
+
Restraint Ladder advisory. Non-Negotiable Carve-Outs mandatory.
|
|
106
|
+
|
|
107
|
+
### full (default)
|
|
108
|
+
Restraint Ladder enforced. Token-Efficiency Rules enforced.
|
|
109
|
+
\`surfil-temper:\` markers required for known ceilings.
|
|
110
|
+
Non-Negotiable Carve-Outs mandatory.
|
|
111
|
+
|
|
112
|
+
### ultra
|
|
113
|
+
Restraint Ladder extremist. Scope challenges active.
|
|
114
|
+
Every addition must justify its existence. Delete speculatively.
|
|
115
|
+
Non-Negotiable Carve-Outs mandatory.
|
|
116
|
+
`;
|
|
117
|
+
const LEVEL_HEADERS = {
|
|
118
|
+
basic: "<!-- surfil-temper level: basic — Restraint Ladder advisory; Non-Negotiable Carve-Outs mandatory -->",
|
|
119
|
+
full: "<!-- surfil-temper level: full (default) — Restraint Ladder enforced; Token-Efficiency Rules enforced; Non-Negotiable Carve-Outs mandatory -->",
|
|
120
|
+
ultra: "<!-- surfil-temper level: ultra — Restraint Ladder extremist; scope challenges active; Non-Negotiable Carve-Outs mandatory -->",
|
|
121
|
+
};
|
|
122
|
+
const VALID_LEVELS = ["basic", "full", "ultra"];
|
|
123
|
+
exports.VALID_LEVELS = VALID_LEVELS;
|
|
124
|
+
/**
|
|
125
|
+
* Returns the Temper Standard text for the given enforcement level.
|
|
126
|
+
* Invalid levels fall back to "full".
|
|
127
|
+
* All levels include the Non-Negotiable Carve-Outs (never stripped).
|
|
128
|
+
*/
|
|
129
|
+
function getStandardForLevel(level) {
|
|
130
|
+
const l = VALID_LEVELS.includes(level)
|
|
131
|
+
? level
|
|
132
|
+
: "full";
|
|
133
|
+
const header = LEVEL_HEADERS[l] ?? LEVEL_HEADERS["full"];
|
|
134
|
+
return header + "\n\n" + STANDARD_FULL;
|
|
135
|
+
}
|
|
136
|
+
exports.TEMPER_VERSION = "1.0.0";
|
|
137
|
+
//# sourceMappingURL=temper-standard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temper-standard.js","sourceRoot":"","sources":["../src/temper-standard.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AA0HH,kDAMC;AA9HD,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwGrB,CAAC;AAEF,MAAM,aAAa,GAA2B;IAC5C,KAAK,EAAG,sGAAsG;IAC9G,IAAI,EAAG,gJAAgJ;IACvJ,KAAK,EAAE,gIAAgI;CACxI,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAiBhD,oCAAY;AAdrB;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,KAAa;IAC/C,MAAM,CAAC,GAAiB,YAAkC,CAAC,QAAQ,CAAC,KAAK,CAAC;QACxE,CAAC,CAAE,KAAqB;QACxB,CAAC,CAAC,MAAM,CAAC;IACX,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,MAAM,CAAE,CAAC;IAC1D,OAAO,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC;AACzC,CAAC;AAEY,QAAA,cAAc,GAAG,OAAO,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "surfil",
|
|
3
|
+
"version": "3.6.7",
|
|
4
|
+
"description": "Surfil MCP server \u2014 bridges MCP-capable editors (Cursor, VS Code, Codex, Zed, Continue, Windsurf, etc.) to the local surfil CLI. Canonical npm name; republishing after npm 24h lock expires.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"surfil",
|
|
7
|
+
"mcp",
|
|
8
|
+
"ai",
|
|
9
|
+
"session-tracking",
|
|
10
|
+
"observability"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://surfil.com",
|
|
13
|
+
"author": "UpgradIQ, Inc.",
|
|
14
|
+
"license": "SEE LICENSE IN https://surfil.com/legal/license",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/UpgradIQ/surfil.git",
|
|
18
|
+
"directory": "surfil-mcp"
|
|
19
|
+
},
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"bin": {
|
|
22
|
+
"surfil-mcp": "dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist/"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.9.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^20.12.0",
|
|
40
|
+
"typescript": "^5.4.5",
|
|
41
|
+
"vitest": "^3.2.0"
|
|
42
|
+
},
|
|
43
|
+
"overrides": {
|
|
44
|
+
"esbuild": "^0.28.1",
|
|
45
|
+
"hono": "^4.12.27"
|
|
46
|
+
}
|
|
47
|
+
}
|