ctxr-cli 0.30.0 → 0.31.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/README.md +28 -0
- package/dist/commands/land.d.ts.map +1 -1
- package/dist/commands/land.js +3 -18
- package/dist/commands/land.js.map +1 -1
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +10 -2
- package/dist/commands/serve.js.map +1 -1
- package/dist/config/schema.d.ts +17 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +40 -8
- package/dist/config/schema.js.map +1 -1
- package/dist/core/browse/approve.d.ts +3 -0
- package/dist/core/browse/approve.d.ts.map +1 -1
- package/dist/core/browse/approve.js.map +1 -1
- package/dist/core/browse/page-url.d.ts +15 -0
- package/dist/core/browse/page-url.d.ts.map +1 -1
- package/dist/core/browse/page-url.js +8 -1
- package/dist/core/browse/page-url.js.map +1 -1
- package/dist/core/forge/credentials.d.ts +12 -1
- package/dist/core/forge/credentials.d.ts.map +1 -1
- package/dist/core/forge/credentials.js +25 -1
- package/dist/core/forge/credentials.js.map +1 -1
- package/dist/core/forge/github.d.ts +9 -0
- package/dist/core/forge/github.d.ts.map +1 -1
- package/dist/core/forge/github.js +6 -0
- package/dist/core/forge/github.js.map +1 -1
- package/dist/core/mcp/drafts.d.ts +30 -0
- package/dist/core/mcp/drafts.d.ts.map +1 -0
- package/dist/core/mcp/drafts.js +52 -0
- package/dist/core/mcp/drafts.js.map +1 -0
- package/dist/core/mcp/http.d.ts +40 -0
- package/dist/core/mcp/http.d.ts.map +1 -0
- package/dist/core/mcp/http.js +90 -0
- package/dist/core/mcp/http.js.map +1 -0
- package/dist/core/mcp/jsonrpc.d.ts +73 -0
- package/dist/core/mcp/jsonrpc.d.ts.map +1 -0
- package/dist/core/mcp/jsonrpc.js +149 -0
- package/dist/core/mcp/jsonrpc.js.map +1 -0
- package/dist/core/mcp/members.d.ts +11 -0
- package/dist/core/mcp/members.d.ts.map +1 -0
- package/dist/core/mcp/members.js +21 -0
- package/dist/core/mcp/members.js.map +1 -0
- package/dist/core/mcp/oauth.d.ts +99 -0
- package/dist/core/mcp/oauth.d.ts.map +1 -0
- package/dist/core/mcp/oauth.js +454 -0
- package/dist/core/mcp/oauth.js.map +1 -0
- package/dist/core/mcp/pages.d.ts +3 -0
- package/dist/core/mcp/pages.d.ts.map +1 -0
- package/dist/core/mcp/pages.js +41 -0
- package/dist/core/mcp/pages.js.map +1 -0
- package/dist/core/mcp/ripgrep.d.ts +48 -0
- package/dist/core/mcp/ripgrep.d.ts.map +1 -0
- package/dist/core/mcp/ripgrep.js +98 -0
- package/dist/core/mcp/ripgrep.js.map +1 -0
- package/dist/core/mcp/rwlock.d.ts +16 -0
- package/dist/core/mcp/rwlock.d.ts.map +1 -0
- package/dist/core/mcp/rwlock.js +59 -0
- package/dist/core/mcp/rwlock.js.map +1 -0
- package/dist/core/mcp/sessions.d.ts +84 -0
- package/dist/core/mcp/sessions.d.ts.map +1 -0
- package/dist/core/mcp/sessions.js +154 -0
- package/dist/core/mcp/sessions.js.map +1 -0
- package/dist/core/mcp/surface.d.ts +42 -0
- package/dist/core/mcp/surface.d.ts.map +1 -0
- package/dist/core/mcp/surface.js +141 -0
- package/dist/core/mcp/surface.js.map +1 -0
- package/dist/core/mcp/sync.d.ts +91 -0
- package/dist/core/mcp/sync.d.ts.map +1 -0
- package/dist/core/mcp/sync.js +218 -0
- package/dist/core/mcp/sync.js.map +1 -0
- package/dist/core/mcp/tools/capture.d.ts +64 -0
- package/dist/core/mcp/tools/capture.d.ts.map +1 -0
- package/dist/core/mcp/tools/capture.js +182 -0
- package/dist/core/mcp/tools/capture.js.map +1 -0
- package/dist/core/mcp/tools/edit.d.ts +79 -0
- package/dist/core/mcp/tools/edit.d.ts.map +1 -0
- package/dist/core/mcp/tools/edit.js +212 -0
- package/dist/core/mcp/tools/edit.js.map +1 -0
- package/dist/core/mcp/tools/files.d.ts +163 -0
- package/dist/core/mcp/tools/files.d.ts.map +1 -0
- package/dist/core/mcp/tools/files.js +265 -0
- package/dist/core/mcp/tools/files.js.map +1 -0
- package/dist/core/mcp/tools/find.d.ts +82 -0
- package/dist/core/mcp/tools/find.d.ts.map +1 -0
- package/dist/core/mcp/tools/find.js +136 -0
- package/dist/core/mcp/tools/find.js.map +1 -0
- package/dist/core/mcp/tools/gather.d.ts +88 -0
- package/dist/core/mcp/tools/gather.d.ts.map +1 -0
- package/dist/core/mcp/tools/gather.js +123 -0
- package/dist/core/mcp/tools/gather.js.map +1 -0
- package/dist/core/mcp/tools/index.d.ts +21 -0
- package/dist/core/mcp/tools/index.d.ts.map +1 -0
- package/dist/core/mcp/tools/index.js +129 -0
- package/dist/core/mcp/tools/index.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/templates/mcp/Edit.md +7 -0
- package/templates/mcp/Glob.md +5 -0
- package/templates/mcp/Grep.md +10 -0
- package/templates/mcp/Read.md +7 -0
- package/templates/mcp/Write.md +7 -0
- package/templates/mcp/capture.md +9 -0
- package/templates/mcp/consent.html +40 -0
- package/templates/mcp/find.md +9 -0
- package/templates/mcp/gather.md +5 -0
- package/templates/mcp/server-instructions.md +17 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { ContextureError } from '../errors.js';
|
|
4
|
+
import { handleMcpRequest, MCP_PATH, sendText } from './http.js';
|
|
5
|
+
import { ToolRefusal } from './jsonrpc.js';
|
|
6
|
+
import { AuthorizationServer, OAUTH_PATHS, Signer } from './oauth.js';
|
|
7
|
+
import { renderConsentPage } from './pages.js';
|
|
8
|
+
import { Mutex } from './sessions.js';
|
|
9
|
+
import { McpView } from './sync.js';
|
|
10
|
+
import { buildTools, serverInstructions } from './tools/index.js';
|
|
11
|
+
/**
|
|
12
|
+
* serve-the-store-over-mcp design.md D3, D10 (remote-access spec): the MCP
|
|
13
|
+
* surface `ctxr serve` routes its MCP paths to. One per server, holding only
|
|
14
|
+
* what must outlive a configuration reload — the read view, the spent-code
|
|
15
|
+
* nonces, and the signer. Everything else is built per request from the
|
|
16
|
+
* state `serve` last loaded, so adding, changing or removing the `mcp` block
|
|
17
|
+
* takes effect on the next request with no restart.
|
|
18
|
+
*/
|
|
19
|
+
/** Every path the MCP surface answers; `serve` routes these here ahead of its own routes. */
|
|
20
|
+
export function isMcpPath(pathname) {
|
|
21
|
+
return pathname === MCP_PATH || OAUTH_PATHS.includes(pathname);
|
|
22
|
+
}
|
|
23
|
+
/** HKDF info for the token-signing key derived from the App's private key (design.md D5). Changing it signs everyone out. */
|
|
24
|
+
const SIGNING_KEY_INFO = 'contexture mcp token signing v1';
|
|
25
|
+
function describeMessage(body) {
|
|
26
|
+
const one = (m) => {
|
|
27
|
+
if (typeof m !== 'object' || m === null)
|
|
28
|
+
return '?';
|
|
29
|
+
const message = m;
|
|
30
|
+
if (message.method === 'tools/call')
|
|
31
|
+
return `tools/call ${typeof message.params?.name === 'string' ? message.params.name : '?'}`;
|
|
32
|
+
return typeof message.method === 'string' ? message.method : 'response';
|
|
33
|
+
};
|
|
34
|
+
return Array.isArray(body) ? `[${body.map(one).join(', ')}]` : one(body);
|
|
35
|
+
}
|
|
36
|
+
export class McpSurface {
|
|
37
|
+
options;
|
|
38
|
+
view;
|
|
39
|
+
spentCodes = new Map();
|
|
40
|
+
mutex = new Mutex();
|
|
41
|
+
signers = new Map();
|
|
42
|
+
randomKey;
|
|
43
|
+
/** Derived once per credential: the key never changes while a credential object lives. */
|
|
44
|
+
derived = new WeakMap();
|
|
45
|
+
repoPrefix;
|
|
46
|
+
constructor(options) {
|
|
47
|
+
this.options = options;
|
|
48
|
+
this.view = new McpView({ git: options.git, canonicalRoot: options.canonicalRoot, now: options.now });
|
|
49
|
+
}
|
|
50
|
+
/** The App-derived key when there is an App; otherwise one random key for this process, said once. */
|
|
51
|
+
signerFor(approval) {
|
|
52
|
+
if (!this.derived.has(approval.credentials))
|
|
53
|
+
this.derived.set(approval.credentials, approval.credentials.derivedSecret(SIGNING_KEY_INFO));
|
|
54
|
+
let key = this.derived.get(approval.credentials) ?? null;
|
|
55
|
+
if (key === null) {
|
|
56
|
+
if (this.randomKey === undefined) {
|
|
57
|
+
this.randomKey = randomBytes(32);
|
|
58
|
+
this.options.log("serve: MCP sign-in tokens are signed with a per-process key (the forge credential is gh's own, not a GitHub App); members sign in again after each restart.");
|
|
59
|
+
}
|
|
60
|
+
key = this.randomKey;
|
|
61
|
+
}
|
|
62
|
+
const id = key.toString('hex');
|
|
63
|
+
let signer = this.signers.get(id);
|
|
64
|
+
if (signer === undefined) {
|
|
65
|
+
signer = new Signer(key);
|
|
66
|
+
this.signers.set(id, signer);
|
|
67
|
+
}
|
|
68
|
+
return signer;
|
|
69
|
+
}
|
|
70
|
+
async handle(state, req, res, pathname) {
|
|
71
|
+
const method = req.method ?? 'GET';
|
|
72
|
+
const config = state.store.config;
|
|
73
|
+
// context-browsing spec: without the block these paths are no different from any unknown path.
|
|
74
|
+
if (config.mcp === undefined) {
|
|
75
|
+
if (method === 'GET' || method === 'HEAD')
|
|
76
|
+
sendText(res, 404, 'not found\n');
|
|
77
|
+
else
|
|
78
|
+
sendText(res, 405, 'method not allowed\n');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (state.approval === null) {
|
|
82
|
+
sendText(res, 503, `The MCP surface is unavailable: ${state.approvalError ?? 'change approval could not be set up'}.\n`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const approval = state.approval;
|
|
86
|
+
const issuer = approval.origin;
|
|
87
|
+
const storeName = path.basename(state.store.root);
|
|
88
|
+
const oauth = new AuthorizationServer({
|
|
89
|
+
issuer,
|
|
90
|
+
resource: `${issuer}${MCP_PATH}`,
|
|
91
|
+
identityHeader: approval.approval.identity.header,
|
|
92
|
+
verifier: approval.verifier,
|
|
93
|
+
mcp: () => config.mcp,
|
|
94
|
+
signer: this.signerFor(approval),
|
|
95
|
+
spentCodes: this.spentCodes,
|
|
96
|
+
now: this.options.now,
|
|
97
|
+
renderConsentPage: (view) => renderConsentPage(storeName, view),
|
|
98
|
+
log: this.options.log,
|
|
99
|
+
});
|
|
100
|
+
if (await oauth.handle(req, res, pathname)) {
|
|
101
|
+
this.options.log(`mcp ${method} ${pathname} -> ${res.statusCode}`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
this.repoPrefix ??= (await this.options.git.run(['rev-parse', '--show-prefix'], { cwd: this.options.canonicalRoot, allowFailure: true })).stdout.trim();
|
|
105
|
+
const syncDeps = { config, credentials: approval.credentials, forge: approval.forge, env: this.options.env };
|
|
106
|
+
const tools = buildTools({
|
|
107
|
+
view: this.view,
|
|
108
|
+
syncDeps,
|
|
109
|
+
write: {
|
|
110
|
+
...syncDeps,
|
|
111
|
+
git: this.options.git,
|
|
112
|
+
canonicalRoot: this.options.canonicalRoot,
|
|
113
|
+
now: this.options.now,
|
|
114
|
+
recordingLogin: approval.approval.recording_login,
|
|
115
|
+
baseUrl: config.serve?.base_url ?? issuer,
|
|
116
|
+
repoPrefix: this.repoPrefix,
|
|
117
|
+
},
|
|
118
|
+
mutex: this.mutex,
|
|
119
|
+
baseUrl: config.serve?.base_url,
|
|
120
|
+
});
|
|
121
|
+
const instructions = serverInstructions(storeName);
|
|
122
|
+
await handleMcpRequest({
|
|
123
|
+
originAllowed: (origin) => origin === issuer,
|
|
124
|
+
authenticate: (r) => oauth.authenticate(r),
|
|
125
|
+
onMessage: (principal, body) => this.options.log(`mcp ${principal.email}: ${describeMessage(body)}`),
|
|
126
|
+
contextFor: (principal) => ({
|
|
127
|
+
principal,
|
|
128
|
+
tools: () => tools.filter((tool) => principal.scopes.includes(tool.scope)),
|
|
129
|
+
lookup: (name) => tools.find((tool) => tool.name === name),
|
|
130
|
+
instructions,
|
|
131
|
+
authorizeCall: (tool) => {
|
|
132
|
+
if (!principal.scopes.includes(tool.scope)) {
|
|
133
|
+
throw new ToolRefusal('mcp.insufficient_scope', `Calling "${tool.name}" needs the "${tool.scope}" scope.`);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
refusalFor: (err) => (err instanceof ContextureError ? new ToolRefusal(err.finding.code, err.finding.message, err.finding.details) : undefined),
|
|
137
|
+
}),
|
|
138
|
+
}, req, res, pathname);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=surface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.js","sourceRoot":"","sources":["../../../src/core/mcp/surface.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { StoreConfig } from '../../config/schema.js';
|
|
2
|
+
import type { ForgeCredentials } from '../forge/credentials.js';
|
|
3
|
+
import type { ForgeClient } from '../forge/github.js';
|
|
4
|
+
import type { GitRunner } from '../git/exec.js';
|
|
5
|
+
import type { Store } from '../store.js';
|
|
6
|
+
/**
|
|
7
|
+
* serve-the-store-over-mcp design.md D6 (remote-access spec, *The surface
|
|
8
|
+
* reads from a checkout of its own*): the canonical checkout `serve` was
|
|
9
|
+
* started on is shared with the agents and moved by `ctxr land`, so the MCP
|
|
10
|
+
* surface never changes it. It reads from a detached worktree of its own
|
|
11
|
+
* under the derived cache path, which it moves by forced checkout to the
|
|
12
|
+
* remote default branch at most once per interval, with its own graph. A
|
|
13
|
+
* reader-writer lock keeps every tool call on one commit; a sync that cannot
|
|
14
|
+
* finish restores the view and its graph together, or refuses calls until a
|
|
15
|
+
* sync works.
|
|
16
|
+
*/
|
|
17
|
+
export declare const DEFAULT_SYNC_INTERVAL_SECONDS = 60;
|
|
18
|
+
/** An inbox file added by a session branch that has an open pull request — a capture awaiting a decision. */
|
|
19
|
+
export interface PendingCapture {
|
|
20
|
+
branch: string;
|
|
21
|
+
/** Store-relative. */
|
|
22
|
+
path: string;
|
|
23
|
+
text: string;
|
|
24
|
+
}
|
|
25
|
+
export interface StoreState {
|
|
26
|
+
head: string;
|
|
27
|
+
sync_error: string | null;
|
|
28
|
+
}
|
|
29
|
+
export interface Snapshot {
|
|
30
|
+
head: string;
|
|
31
|
+
/** The view as a store: its own root, and the configuration committed at `head`. */
|
|
32
|
+
store: Store;
|
|
33
|
+
pending: readonly PendingCapture[];
|
|
34
|
+
}
|
|
35
|
+
/** What a sync needs from the server's current state — re-read per request, so configuration reloads apply. */
|
|
36
|
+
export interface SyncDeps {
|
|
37
|
+
/** The configuration `serve` loaded for the canonical checkout (session prefix, default branch). */
|
|
38
|
+
config: StoreConfig;
|
|
39
|
+
credentials: ForgeCredentials;
|
|
40
|
+
forge: ForgeClient;
|
|
41
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
42
|
+
}
|
|
43
|
+
export interface ViewOptions {
|
|
44
|
+
git: GitRunner;
|
|
45
|
+
/** The store root `serve` was started on. */
|
|
46
|
+
canonicalRoot: string;
|
|
47
|
+
intervalSeconds?: number;
|
|
48
|
+
now(): Date;
|
|
49
|
+
}
|
|
50
|
+
/** Where the view's worktree lives: under the canonical checkout's derived cache, beside the graph. */
|
|
51
|
+
export declare function viewWorktreeDir(canonicalRoot: string): string;
|
|
52
|
+
export declare class McpView {
|
|
53
|
+
private readonly options;
|
|
54
|
+
private readonly lock;
|
|
55
|
+
private readonly intervalMs;
|
|
56
|
+
private head;
|
|
57
|
+
private lastError;
|
|
58
|
+
private unavailable;
|
|
59
|
+
private lastSyncAt;
|
|
60
|
+
private syncing;
|
|
61
|
+
private pending;
|
|
62
|
+
private viewStore;
|
|
63
|
+
/** The canonical store root's path inside its repository ("" at the top level, else "sub/dir/"). */
|
|
64
|
+
private repoPrefix;
|
|
65
|
+
constructor(options: ViewOptions);
|
|
66
|
+
private run;
|
|
67
|
+
get state(): StoreState;
|
|
68
|
+
/** Runs `fn` against one commit, after a sync if one is due, and stamps the result with what it read. */
|
|
69
|
+
read<T extends object>(deps: SyncDeps, fn: (snapshot: Snapshot) => Promise<T>): Promise<T & {
|
|
70
|
+
store: StoreState;
|
|
71
|
+
}>;
|
|
72
|
+
/** For `capture`: a sync forced now, so the duplicate check sees what was pushed and opened a moment ago (design.md D9). */
|
|
73
|
+
refresh(deps: SyncDeps): Promise<void>;
|
|
74
|
+
private maybeSync;
|
|
75
|
+
private ensureView;
|
|
76
|
+
/** Exclusive: no tool call is reading while this runs. */
|
|
77
|
+
private syncOnce;
|
|
78
|
+
/** Forced, detached, and cleaned of untracked files — the view is this surface's alone. Null on success. */
|
|
79
|
+
private checkout;
|
|
80
|
+
/** The configuration committed at the view's commit, and that commit's graph. */
|
|
81
|
+
private rebuild;
|
|
82
|
+
/** Back to `commit` with its graph; null on success, else why not. */
|
|
83
|
+
private restore;
|
|
84
|
+
/**
|
|
85
|
+
* design.md D6–D7: "pending" means what the approval index means by it — a
|
|
86
|
+
* session branch with an open pull request. Returns an error line when the
|
|
87
|
+
* forge could not be read, keeping the previous list.
|
|
88
|
+
*/
|
|
89
|
+
private readPending;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/sync.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAMhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAIzC;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,6GAA6G;AAC7G,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,oFAAoF;IACpF,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;CACpC;AAED,+GAA+G;AAC/G,MAAM,WAAW,QAAQ;IACvB,oGAAoG;IACpG,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,KAAK,EAAE,WAAW,CAAC;IACnB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,SAAS,CAAC;IACf,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,IAAI,IAAI,CAAC;CACb;AAMD,uGAAuG;AACvG,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,qBAAa,OAAO;IAaN,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,SAAS,CAAoB;IACrC,oGAAoG;IACpG,OAAO,CAAC,UAAU,CAAqB;IAEvC,YAA6B,OAAO,EAAE,WAAW,EAEhD;IAED,OAAO,CAAC,GAAG;IAIX,IAAI,KAAK,IAAI,UAAU,CAEtB;IAED,yGAAyG;IACnG,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG;QAAE,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC,CASvH;IAED,4HAA4H;IACtH,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3C;YAEa,SAAS;YAUT,UAAU;IAaxB,0DAA0D;YAC5C,QAAQ;IAwDtB,4GAA4G;YAC9F,QAAQ;IAOtB,iFAAiF;YACnE,OAAO;IAUrB,sEAAsE;YACxD,OAAO;IAWrB;;;;OAIG;YACW,WAAW;CA2B1B"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { readConfig } from '../../config/load.js';
|
|
4
|
+
import { MIGRATION_STEPS } from '../../config/migrations.js';
|
|
5
|
+
import { sessionWorktrees } from '../approval/sessions.js';
|
|
6
|
+
import { runRemoteGit } from '../forge/credentials.js';
|
|
7
|
+
import { isUnderAnyPrefix } from '../fs/prefix.js';
|
|
8
|
+
import { pruneWorktrees } from '../git/worktree.js';
|
|
9
|
+
import { renderGraphDocument } from '../graph/document.js';
|
|
10
|
+
import { buildGraphFromNotes, graphBuildOptions } from '../graph/model.js';
|
|
11
|
+
import { readGraph, writeGraph, writeGraphDocument } from '../graph/persist.js';
|
|
12
|
+
import { listNotes } from '../notes/list.js';
|
|
13
|
+
import { ToolRefusal } from './jsonrpc.js';
|
|
14
|
+
import { ReadWriteLock } from './rwlock.js';
|
|
15
|
+
/**
|
|
16
|
+
* serve-the-store-over-mcp design.md D6 (remote-access spec, *The surface
|
|
17
|
+
* reads from a checkout of its own*): the canonical checkout `serve` was
|
|
18
|
+
* started on is shared with the agents and moved by `ctxr land`, so the MCP
|
|
19
|
+
* surface never changes it. It reads from a detached worktree of its own
|
|
20
|
+
* under the derived cache path, which it moves by forced checkout to the
|
|
21
|
+
* remote default branch at most once per interval, with its own graph. A
|
|
22
|
+
* reader-writer lock keeps every tool call on one commit; a sync that cannot
|
|
23
|
+
* finish restores the view and its graph together, or refuses calls until a
|
|
24
|
+
* sync works.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_SYNC_INTERVAL_SECONDS = 60;
|
|
27
|
+
function firstLine(text) {
|
|
28
|
+
return text.trim().split('\n')[0] ?? '';
|
|
29
|
+
}
|
|
30
|
+
/** Where the view's worktree lives: under the canonical checkout's derived cache, beside the graph. */
|
|
31
|
+
export function viewWorktreeDir(canonicalRoot) {
|
|
32
|
+
return path.join(canonicalRoot, '.contexture', 'cache', 'mcp', 'view');
|
|
33
|
+
}
|
|
34
|
+
export class McpView {
|
|
35
|
+
options;
|
|
36
|
+
lock = new ReadWriteLock();
|
|
37
|
+
intervalMs;
|
|
38
|
+
head = '';
|
|
39
|
+
lastError = null;
|
|
40
|
+
unavailable = false;
|
|
41
|
+
lastSyncAt;
|
|
42
|
+
syncing;
|
|
43
|
+
pending = [];
|
|
44
|
+
viewStore;
|
|
45
|
+
/** The canonical store root's path inside its repository ("" at the top level, else "sub/dir/"). */
|
|
46
|
+
repoPrefix;
|
|
47
|
+
constructor(options) {
|
|
48
|
+
this.options = options;
|
|
49
|
+
this.intervalMs = (options.intervalSeconds ?? DEFAULT_SYNC_INTERVAL_SECONDS) * 1000;
|
|
50
|
+
}
|
|
51
|
+
run(args, cwd) {
|
|
52
|
+
return this.options.git.run(args, { cwd, allowFailure: true });
|
|
53
|
+
}
|
|
54
|
+
get state() {
|
|
55
|
+
return { head: this.head, sync_error: this.lastError };
|
|
56
|
+
}
|
|
57
|
+
/** Runs `fn` against one commit, after a sync if one is due, and stamps the result with what it read. */
|
|
58
|
+
async read(deps, fn) {
|
|
59
|
+
await this.maybeSync(deps, false);
|
|
60
|
+
return this.lock.read(async () => {
|
|
61
|
+
if (this.unavailable || this.viewStore === undefined) {
|
|
62
|
+
throw new ToolRefusal('mcp.store_unavailable', `The store cannot be read until a sync succeeds: ${this.lastError ?? 'it has not been read yet'}.`);
|
|
63
|
+
}
|
|
64
|
+
const result = await fn({ head: this.head, store: this.viewStore, pending: this.pending });
|
|
65
|
+
return { ...result, store: this.state };
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** For `capture`: a sync forced now, so the duplicate check sees what was pushed and opened a moment ago (design.md D9). */
|
|
69
|
+
async refresh(deps) {
|
|
70
|
+
await this.maybeSync(deps, true);
|
|
71
|
+
}
|
|
72
|
+
async maybeSync(deps, force) {
|
|
73
|
+
const now = this.options.now().getTime();
|
|
74
|
+
const due = force || this.lastSyncAt === undefined || now - this.lastSyncAt >= this.intervalMs;
|
|
75
|
+
if (!due)
|
|
76
|
+
return;
|
|
77
|
+
this.syncing ??= this.lock.write(() => this.syncOnce(deps)).finally(() => {
|
|
78
|
+
this.syncing = undefined;
|
|
79
|
+
});
|
|
80
|
+
await this.syncing;
|
|
81
|
+
}
|
|
82
|
+
async ensureView(startRef) {
|
|
83
|
+
const canonical = this.options.canonicalRoot;
|
|
84
|
+
this.repoPrefix ??= (await this.run(['rev-parse', '--show-prefix'], canonical)).stdout.trim();
|
|
85
|
+
const dir = viewWorktreeDir(canonical);
|
|
86
|
+
if (!existsSync(path.join(dir, '.git'))) {
|
|
87
|
+
// A view deleted with the derived cache leaves a registration behind; clear it before adding another.
|
|
88
|
+
await pruneWorktrees(this.options.git, canonical);
|
|
89
|
+
const added = await this.run(['worktree', 'add', '--detach', '--force', dir, startRef], canonical);
|
|
90
|
+
if (added.exitCode !== 0)
|
|
91
|
+
throw new Error(`could not create the read view: ${firstLine(added.stderr)}`);
|
|
92
|
+
}
|
|
93
|
+
return path.join(dir, this.repoPrefix);
|
|
94
|
+
}
|
|
95
|
+
/** Exclusive: no tool call is reading while this runs. */
|
|
96
|
+
async syncOnce(deps) {
|
|
97
|
+
this.lastSyncAt = this.options.now().getTime();
|
|
98
|
+
const canonical = this.options.canonicalRoot;
|
|
99
|
+
const branch = deps.config.git.default_branch;
|
|
100
|
+
const remoteRef = `refs/remotes/origin/${branch}`;
|
|
101
|
+
const fetched = await runRemoteGit(this.options.git, deps.credentials, deps.env, ['fetch', '--quiet', 'origin', `+refs/heads/${branch}:${remoteRef}`], {
|
|
102
|
+
cwd: canonical,
|
|
103
|
+
allowFailure: true,
|
|
104
|
+
});
|
|
105
|
+
const fetchError = fetched.exitCode === 0 ? null : `fetch failed: ${firstLine(fetched.stderr) || `exit ${fetched.exitCode}`}`;
|
|
106
|
+
const hasRemoteRef = (await this.run(['rev-parse', '--verify', '--quiet', `${remoteRef}^{commit}`], canonical)).exitCode === 0;
|
|
107
|
+
const target = hasRemoteRef ? remoteRef : branch;
|
|
108
|
+
let viewRoot;
|
|
109
|
+
try {
|
|
110
|
+
viewRoot = await this.ensureView(target);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
this.lastError = err instanceof Error ? err.message : String(err);
|
|
114
|
+
this.unavailable = this.viewStore === undefined;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const previous = this.head;
|
|
118
|
+
const moved = await this.checkout(viewRoot, target);
|
|
119
|
+
if (moved !== null) {
|
|
120
|
+
this.lastError = moved;
|
|
121
|
+
this.unavailable = this.viewStore === undefined;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const next = (await this.run(['rev-parse', 'HEAD'], viewRoot)).stdout.trim();
|
|
125
|
+
if (next !== previous || this.viewStore === undefined || (await readGraph(this.viewStore)) === null) {
|
|
126
|
+
try {
|
|
127
|
+
this.viewStore = await this.rebuild(viewRoot);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
131
|
+
const restored = previous === '' ? 'nothing to restore' : await this.restore(viewRoot, previous);
|
|
132
|
+
if (restored === null) {
|
|
133
|
+
this.lastError = `the read view could not be built at ${next.slice(0, 12)} (${reason}); serving ${previous.slice(0, 12)}`;
|
|
134
|
+
this.unavailable = false;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
this.lastError = `the read view could not be built at ${next.slice(0, 12)} (${reason}), and restoring the previous commit failed (${restored})`;
|
|
138
|
+
this.unavailable = true;
|
|
139
|
+
}
|
|
140
|
+
await this.readPending(deps);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
this.head = next;
|
|
145
|
+
this.unavailable = false;
|
|
146
|
+
const pendingError = await this.readPending(deps);
|
|
147
|
+
this.lastError = fetchError ?? pendingError;
|
|
148
|
+
}
|
|
149
|
+
/** Forced, detached, and cleaned of untracked files — the view is this surface's alone. Null on success. */
|
|
150
|
+
async checkout(viewRoot, ref) {
|
|
151
|
+
const checkedOut = await this.run(['checkout', '--quiet', '--detach', '--force', ref], viewRoot);
|
|
152
|
+
if (checkedOut.exitCode !== 0)
|
|
153
|
+
return `the read view could not check out ${ref}: ${firstLine(checkedOut.stderr)}`;
|
|
154
|
+
await this.run(['clean', '-fdq'], viewRoot);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
/** The configuration committed at the view's commit, and that commit's graph. */
|
|
158
|
+
async rebuild(viewRoot) {
|
|
159
|
+
const config = await readConfig(viewRoot, MIGRATION_STEPS);
|
|
160
|
+
const store = { root: viewRoot, config };
|
|
161
|
+
const notes = await listNotes(store.root, store.config);
|
|
162
|
+
const graph = buildGraphFromNotes(notes, graphBuildOptions(store.config));
|
|
163
|
+
await writeGraph(store, graph);
|
|
164
|
+
await writeGraphDocument(store, renderGraphDocument(graph, store.config.retrieval.graph).text);
|
|
165
|
+
return store;
|
|
166
|
+
}
|
|
167
|
+
/** Back to `commit` with its graph; null on success, else why not. */
|
|
168
|
+
async restore(viewRoot, commit) {
|
|
169
|
+
const failed = await this.checkout(viewRoot, commit);
|
|
170
|
+
if (failed !== null)
|
|
171
|
+
return failed;
|
|
172
|
+
try {
|
|
173
|
+
this.viewStore = await this.rebuild(viewRoot);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
return err instanceof Error ? err.message : String(err);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* design.md D6–D7: "pending" means what the approval index means by it — a
|
|
182
|
+
* session branch with an open pull request. Returns an error line when the
|
|
183
|
+
* forge could not be read, keeping the previous list.
|
|
184
|
+
*/
|
|
185
|
+
async readPending(deps) {
|
|
186
|
+
let open;
|
|
187
|
+
try {
|
|
188
|
+
open = new Set((await deps.forge.openPulls()).map((p) => p.headRef));
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
return `open pull requests could not be read: ${err instanceof Error ? firstLine(err.message) : String(err)}`;
|
|
192
|
+
}
|
|
193
|
+
const canonical = this.options.canonicalRoot;
|
|
194
|
+
const prefix = this.repoPrefix ?? '';
|
|
195
|
+
const inbox = deps.config.ingest.inbox_path;
|
|
196
|
+
const base = `refs/remotes/origin/${deps.config.git.default_branch}`;
|
|
197
|
+
const found = [];
|
|
198
|
+
const sessions = (await sessionWorktrees(this.options.git, canonical, deps.config)).filter((s) => open.has(s.branch));
|
|
199
|
+
for (const session of sessions.sort((a, b) => (a.branch < b.branch ? -1 : 1))) {
|
|
200
|
+
const added = await this.run(['diff', '--name-only', '--diff-filter=A', '--no-renames', `${base}...${session.branch}`], canonical);
|
|
201
|
+
if (added.exitCode !== 0)
|
|
202
|
+
continue;
|
|
203
|
+
for (const repoPath of added.stdout.split('\n').filter((p) => p.length > 0)) {
|
|
204
|
+
if (!repoPath.startsWith(prefix))
|
|
205
|
+
continue;
|
|
206
|
+
const filePath = repoPath.slice(prefix.length);
|
|
207
|
+
if (!filePath.endsWith('.md') || !isUnderAnyPrefix(filePath, [inbox]))
|
|
208
|
+
continue;
|
|
209
|
+
const shown = await this.run(['show', `${session.branch}:${repoPath}`], canonical);
|
|
210
|
+
if (shown.exitCode === 0)
|
|
211
|
+
found.push({ branch: session.branch, path: filePath, text: shown.stdout });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
this.pending = found;
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../../src/core/mcp/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type WriteDeps } from '../sessions.js';
|
|
2
|
+
import type { McpView, SyncDeps } from '../sync.js';
|
|
3
|
+
/**
|
|
4
|
+
* remote-access spec, *`capture` opens a session pull request and never
|
|
5
|
+
* writes the default branch* (design.md D9). One new inbox file, on its own
|
|
6
|
+
* session, pushed as the App, with a pull request the approval page lists. A
|
|
7
|
+
* capture is idempotent by source identity — derived from the content when
|
|
8
|
+
* the caller names none — so a call that failed after committing resumes into
|
|
9
|
+
* the same pull request instead of being refused as its own duplicate.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MAX_CAPTURE_BODY_BYTES: number;
|
|
12
|
+
export declare const CAPTURE_LABEL_PREFIX = "capture-";
|
|
13
|
+
export interface CaptureArgs {
|
|
14
|
+
title: string;
|
|
15
|
+
body: string;
|
|
16
|
+
source_type: string;
|
|
17
|
+
source_id?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Plain on purpose (see FIND_INPUT_SCHEMA): `parseCaptureArgs` enforces every limit. */
|
|
20
|
+
export declare const CAPTURE_INPUT_SCHEMA: {
|
|
21
|
+
readonly type: 'object';
|
|
22
|
+
readonly properties: {
|
|
23
|
+
readonly title: {
|
|
24
|
+
readonly type: 'string';
|
|
25
|
+
readonly description: 'A short title naming the item, e.g. "Pricing review call, 14 October".';
|
|
26
|
+
};
|
|
27
|
+
readonly body: {
|
|
28
|
+
readonly type: 'string';
|
|
29
|
+
readonly description: 'The material itself, in its own words, as markdown.';
|
|
30
|
+
};
|
|
31
|
+
readonly source_type: {
|
|
32
|
+
readonly type: 'string';
|
|
33
|
+
readonly description: 'Where it came from, lowercased: email, meeting, web, document, …';
|
|
34
|
+
};
|
|
35
|
+
readonly source_id: {
|
|
36
|
+
readonly type: 'string';
|
|
37
|
+
readonly description: "The source system's own id for the item (a message id, a meeting id, a URL), when there is one.";
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
readonly required: readonly ['title', 'body', 'source_type'];
|
|
41
|
+
};
|
|
42
|
+
export declare function parseCaptureArgs(args: Record<string, unknown>): CaptureArgs;
|
|
43
|
+
export interface CaptureResult {
|
|
44
|
+
saved: true;
|
|
45
|
+
resumed: boolean;
|
|
46
|
+
path: string;
|
|
47
|
+
source_id: string;
|
|
48
|
+
pull_request: {
|
|
49
|
+
number: number;
|
|
50
|
+
url: string;
|
|
51
|
+
};
|
|
52
|
+
approval_url: string;
|
|
53
|
+
message: string;
|
|
54
|
+
}
|
|
55
|
+
export interface CaptureContext {
|
|
56
|
+
view: McpView;
|
|
57
|
+
syncDeps: SyncDeps;
|
|
58
|
+
write: WriteDeps;
|
|
59
|
+
}
|
|
60
|
+
/** Run under the write mutex (design.md D9). */
|
|
61
|
+
export declare function capture(ctx: CaptureContext, email: string, args: CaptureArgs): Promise<CaptureResult>;
|
|
62
|
+
/** WriteRefusal → ToolRefusal, so the client reads the reason rather than a protocol error. */
|
|
63
|
+
export declare function asToolRefusal(err: unknown): unknown;
|
|
64
|
+
//# sourceMappingURL=capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../../../src/core/mcp/tools/capture.ts"],"names":[],"mappings":"AAaA,OAAO,EAWL,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;GAOG;AAEH,eAAO,MAAM,sBAAsB,QAAa,CAAC;AAGjD,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAE/C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,yFAAyF;AACzF,eAAO,MAAM,oBAAoB;aAC/B,IAAI,EAAE,QAAQ;aACd,UAAU;iBACR,KAAK;qBAAI,IAAI,EAAE,QAAQ;qBAAE,WAAW,EAAE,wEAAwE;;iBAC9G,IAAI;qBAAI,IAAI,EAAE,QAAQ;qBAAE,WAAW,EAAE,qDAAqD;;iBAC1F,WAAW;qBAAI,IAAI,EAAE,QAAQ;qBAAE,WAAW,EAAE,kEAAkE;;iBAC9G,SAAS;qBAAI,IAAI,EAAE,QAAQ;qBAAE,WAAW,EAAE,iGAAiG;;;aAE7I,QAAQ,YAAG,OAAO,EAAE,MAAM,EAAE,aAAa;CACjC,CAAC;AAEX,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW,CAkB3E;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,IAAI,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;CAClB;AAoDD,gDAAgD;AAChD,wBAAsB,OAAO,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAkE3G;AAED,+FAA+F;AAC/F,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEnD"}
|