@reunionstudio/airlock-mcp 0.1.2 → 0.1.4

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.
@@ -16,8 +16,9 @@ This repo is an Airlock MCP workspace for drafting Airlock specs with Codex.
16
16
  The public install surface is Airlock MCP, for example
17
17
  `npx @reunionstudio/airlock-mcp install`. Airlock MCP is the single installed
18
18
  interface for building specs, using specs, pulling and pushing governed data,
19
- and discovering improvements from real use cases. This project repo remains
20
- the durable memory for the specs being drafted.
19
+ building apps or workflows that use existing specs, and discovering
20
+ improvements from real use cases. This project repo remains the durable memory
21
+ for the specs being drafted.
21
22
 
22
23
  ## Repo Naming
23
24
 
@@ -45,7 +46,13 @@ installed Airlock app.
45
46
  ## Starting A Spec Project
46
47
 
47
48
  After bootstrap, welcome the user and orient before creating a workspace. Start
48
- with:
49
+ by asking which delivery mode they want:
50
+
51
+ 1. Spec-first: design governed specs before building the app surface.
52
+ 2. App-first: build from existing specs.
53
+ 3. Co-development: develop the app and specs together.
54
+
55
+ For spec-first or co-development work, ask:
49
56
 
50
57
  What process do you want to improve?
51
58
 
@@ -79,10 +86,64 @@ plan for more. Do not create the first workspace until the user chooses a path.
79
86
  Create `posts` only when the user wants a shared feedback loop or explicitly
80
87
  asks for the posts pattern.
81
88
 
89
+ ## Building Apps With Existing Specs
90
+
91
+ Use this path when the user wants to build an app, dashboard, approval queue,
92
+ decision UI, analysis workflow, scheduled agent, or other code that uses specs
93
+ they already have access to. Treat this as app/workflow implementation, not
94
+ spec editing, unless the user explicitly asks to change specs.
95
+
96
+ Use `airlock-mcp init-app-context` in an app repo when the app needs local
97
+ Airlock context. It creates `airlock/specs.manifest.json`,
98
+ `airlock/spec-snapshots/`, `airlock/sample-records/`, and generated helper
99
+ folders. These files are app-local references, not canonical specs. Canonical
100
+ specs live in the specs repo or installed Airlock.
101
+
102
+ Identify:
103
+
104
+ - the app goal and decision the app should support
105
+ - read specs such as budgets, expenses, requests, forecasts, observations,
106
+ payouts, or reference data
107
+ - write specs such as decisions, approvals, comments, commitments, actions, or
108
+ follow-ups
109
+ - where the app should run, such as Streamlit, web app, CLI, notebook,
110
+ scheduled agent, or existing app framework
111
+ - the Airlock access path available in this environment
112
+ - identity, evidence, timestamp, approval, and separation-of-duties rules
113
+
114
+ Installed Airlock separates procedure intent:
115
+
116
+ - `observe.*`: read-only governance observation for discovery, health, access
117
+ explanation, governance maps, activity, billing events, and context packets.
118
+ - `agent.*`: governed agent work in the actor's scope.
119
+ - `admin.*`: administrative mutation and operational changes.
120
+
121
+ For app-first work, use observe payloads such as `observe.procedures`,
122
+ `observe.specs`, `observe.spec`, `observe.governance_map`,
123
+ `observe.explain_access`, `observe.health`, `observe.activity`,
124
+ `observe.admin_activity`, `observe.spec_admin_activity`, and
125
+ `observe.billing_events` before inventing custom read paths. For `alter_spec`
126
+ activity, use `CHANGED_SECTIONS` and `CHANGED_FIELDS` to triage what changed
127
+ before fetching version snapshots. Do not call retired admin read wrappers such as `admin.list_specs`, `admin.describe_role`,
128
+ or `admin.list_events`; use the matching observe list/detail procedure.
129
+
130
+ The app may orient the user with summaries, comparisons, rankings, exception
131
+ queues, proposals, or dashboards. It should submit governed choices back
132
+ through an Airlock spec contract. Do not write directly to Airlock-owned tables,
133
+ stages, generated views, or generated tables. Do not bypass spec workflow just
134
+ because the app can connect to Snowflake. If no suitable decision or action spec
135
+ exists, explain the gap and propose a small spec-design step.
136
+
137
+ In co-development mode, keep two tracks visible:
138
+
139
+ - Spec track: row grain, columns, samples, access, validation, workflow
140
+ - App track: screens, reads, decisions, writes, user actions, runtime
141
+
82
142
  ## Working Style
83
143
 
84
144
  - Use the repo-scoped `$airlock-mcp` skill for spec drafting, review, and
85
- pattern selection.
145
+ pattern selection, or for app/workflow implementation that uses existing
146
+ Airlock specs.
86
147
  - Keep drafts small and concrete. Prefer one useful governed output over a
87
148
  large speculative process map, then keep a plan for later specs.
88
149
  - Preserve decisions in workspace files so future Codex sessions can resume
@@ -5,6 +5,7 @@ import sys
5
5
  from pathlib import Path
6
6
 
7
7
  from . import __version__
8
+ from .app_context import APP_CONTEXT_MODES, format_app_context_result, init_app_context
8
9
  from .art import about_text
9
10
  from .bootstrap import bootstrap_repo, format_bootstrap_result
10
11
  from .jsonio import read_json
@@ -64,6 +65,17 @@ def init_repo(args: argparse.Namespace) -> int:
64
65
  return 0
65
66
 
66
67
 
68
+ def init_app(args: argparse.Namespace) -> int:
69
+ result = init_app_context(
70
+ Path(args.path),
71
+ mode=args.mode,
72
+ spec_sources=[Path(source) for source in args.spec],
73
+ force=args.force,
74
+ )
75
+ print(format_app_context_result(result))
76
+ return 0
77
+
78
+
67
79
  def init_workspace(args: argparse.Namespace) -> int:
68
80
  patterns = load_patterns(repo_root())
69
81
  pattern = patterns[args.pattern]
@@ -308,6 +320,26 @@ def build_parser() -> argparse.ArgumentParser:
308
320
  init_repo_parser.add_argument("--force", action="store_true", help="Overwrite AGENTS.md and the repo-scoped skill.")
309
321
  init_repo_parser.set_defaults(func=init_repo)
310
322
 
323
+ init_app_parser = subparsers.add_parser(
324
+ "init-app-context",
325
+ help="Prepare an app repo airlock/ context with spec snapshots and a manifest.",
326
+ )
327
+ init_app_parser.add_argument("path", nargs="?", default=".", help="App repo path. Defaults to current directory.")
328
+ init_app_parser.add_argument(
329
+ "--mode",
330
+ choices=APP_CONTEXT_MODES,
331
+ default="app-first",
332
+ help="Development mode. Defaults to app-first.",
333
+ )
334
+ init_app_parser.add_argument(
335
+ "--spec",
336
+ action="append",
337
+ default=[],
338
+ help="Spec workspace directory or spec JSON file to snapshot. Repeat for multiple specs.",
339
+ )
340
+ init_app_parser.add_argument("--force", action="store_true", help="Overwrite app context files.")
341
+ init_app_parser.set_defaults(func=init_app)
342
+
311
343
  init_parser = subparsers.add_parser("init", help="Create a spec workspace.")
312
344
  init_parser.add_argument("name", help="Workspace folder name.")
313
345
  init_parser.add_argument(
@@ -247,6 +247,7 @@ def workspace_summary(
247
247
  f" owner_role: {_format_value(core.get('owner_role'))}",
248
248
  f" published: {_format_value(core.get('is_published'))}",
249
249
  f" archived: {_format_value(core.get('is_archived'))}",
250
+ f" payload_adapter: {_format_value(core.get('payload_adapter'), default='default')}",
250
251
  f" description: {_truncate(core.get('description')) or 'none'}",
251
252
  "",
252
253
  "file_rules:",
package/src/mcp.mjs CHANGED
@@ -7,7 +7,7 @@ const GETTING_STARTED_URI = "airlock://getting-started";
7
7
 
8
8
  const START_TOOL = {
9
9
  name: "airlock_start",
10
- description: "Return Airlock MCP setup guidance for building and using Airlock specs.",
10
+ description: "Return Airlock MCP setup guidance for specs and apps that use specs.",
11
11
  inputSchema: {
12
12
  type: "object",
13
13
  properties: {
@@ -44,10 +44,10 @@ export function handleMcpRequest(message) {
44
44
  },
45
45
  serverInfo: {
46
46
  name: "airlock",
47
- version: "0.1.2",
47
+ version: "0.1.4",
48
48
  },
49
49
  instructions:
50
- "Airlock MCP helps agents improve processes with Airlock specs. Use airlock_start for orientation or the airlock_* tools to bootstrap, draft, check, summarize, export, and render specs.",
50
+ "Airlock MCP helps agents improve processes with Airlock specs and build apps or workflows that use existing specs. Use airlock_start for orientation or the airlock_* tools to bootstrap, draft, check, summarize, export, and render specs.",
51
51
  });
52
52
  }
53
53
 
@@ -84,7 +84,7 @@ export function handleMcpRequest(message) {
84
84
  {
85
85
  name: "airlock-start",
86
86
  title: "Start Airlock",
87
- description: "Bootstrap a Git-backed specs repo and choose the first Airlock path.",
87
+ description: "Bootstrap a Git-backed specs repo or choose an Airlock app/spec path.",
88
88
  arguments: [
89
89
  {
90
90
  name: "project",
@@ -103,7 +103,7 @@ export function handleMcpRequest(message) {
103
103
  }
104
104
  const project = params?.arguments?.project || "Home";
105
105
  return makeResponse(id, {
106
- description: "Start building and using Airlock specs in a Git-backed specs repo.",
106
+ description: "Start building specs or apps that use Airlock specs.",
107
107
  messages: [
108
108
  {
109
109
  role: "user",
package/src/text.mjs CHANGED
@@ -25,19 +25,23 @@ Set up this project as an Airlock specs repo. If this project is not already a
25
25
  Git repo, recommend storing it in version control, preferably GitHub if that is
26
26
  available. If you are creating the repo for me, ask where the ${repoName}
27
27
  directory should live before making it, then offer to initialize git and create
28
- or push a GitHub repo.
29
-
30
- Welcome me by asking what process I want to improve. Explain that Airlock works
28
+ or push a GitHub repo. If I am in an app repo and want to build software that
29
+ uses existing specs, or develop the app and specs together, do not bootstrap a
30
+ specs repo unless I ask for spec edits.
31
+
32
+ Welcome me by asking whether I want spec-first, app-first, or co-development:
33
+ spec-first designs governed specs before the app, app-first builds from existing
34
+ specs, and co-development evolves the app and specs together. For spec-first or
35
+ co-development, ask what process I want to improve. Explain that Airlock works
31
36
  best when we can identify the loop around that process: what information comes
32
37
  in, what context helps us understand it, what decision needs to be made, and
33
38
  what action happens after the decision. Information may come from apps, files,
34
- forms, people,
35
- emails, calls, mail, websites, APIs, data feeds, or physical events. Actions
36
- may go back through those same places. Ask whether I already have artifacts:
37
- CSV or Excel files, JSON samples, API docs, schemas, forms, screenshots, PDFs,
38
- exports, message examples, or other content people already use. Treat a small
39
- real sample as stronger evidence than a long explanation, and remind me to
40
- redact secrets.
39
+ forms, people, emails, calls, mail, websites, APIs, data feeds, or physical
40
+ events. Actions may go back through those same places. Ask whether I already
41
+ have artifacts: CSV or Excel files, JSON samples, API docs, schemas, forms,
42
+ screenshots, PDFs, exports, message examples, or other content people already
43
+ use. Treat a small real sample as stronger evidence than a long explanation,
44
+ and remind me to redact secrets.
41
45
 
42
46
  When useful, check the reusable airlock-specs library for starting points,
43
47
  patterns, and ideas, but do not assume those library specs match current
@@ -45,7 +49,26 @@ third-party systems. Prefer current API docs, real exports, samples, and other
45
49
  artifacts when they conflict with a library shape.
46
50
 
47
51
  Ask for the messy version, then help turn it into a small first Airlock spec
48
- and a plan for more. Do not create the first workspace until I choose a path.`;
52
+ and a plan for more. Do not create the first workspace until I choose a path.
53
+
54
+ If I want app-first or co-development, ask for the app goal, the specs I can
55
+ access, which specs are read sources, which spec records decisions or actions,
56
+ and where the app should run. Teach the installed Airlock procedure split:
57
+ \`airlock.observe.*\` is read-only governance observation,
58
+ \`airlock.agent.*\` is governed agent work, and \`airlock.admin.*\` is
59
+ administrative mutation. Prefer observe payloads such as
60
+ \`observe.procedures\`, \`observe.specs\`, \`observe.spec\`,
61
+ \`observe.governance_map\`, \`observe.explain_access\`, \`observe.health\`,
62
+ \`observe.activity\`, \`observe.admin_activity\`, \`observe.spec_admin_activity\`,
63
+ and \`observe.billing_events\` before inventing custom read paths. For \`alter_spec\`
64
+ activity, use \`CHANGED_SECTIONS\` and \`CHANGED_FIELDS\` to triage what changed
65
+ before fetching version snapshots. Offer to run \`airlock-mcp init-app-context\`
66
+ in the app repo to seed \`airlock/specs.manifest.json\`, spec snapshots, sample records, and
67
+ generated helper folders. Help code the app using approved Airlock/Snowflake access paths.
68
+ Do not use retired admin read wrappers such as
69
+ \`admin.list_specs\`, \`admin.describe_role\`, or \`admin.list_events\`; use
70
+ the matching observe procedure. Do not write directly to Airlock-owned tables
71
+ or bypass spec workflow.`;
49
72
  }
50
73
 
51
74
  export function nextSteps(project) {
@@ -63,11 +86,16 @@ ${airlockPrompt(project)
63
86
 
64
87
  Airlock MCP will offer:
65
88
  - process discovery before choosing a spec pattern
89
+ - spec-first, app-first, and co-development planning
66
90
  - spec design with the bundled workbench
67
91
  - Airlock operating patterns for OODA loops and separation of duties
92
+ - read-only observe procedures for governance maps, health, access explanation, activity, billing events, and context packets
93
+ - app context seeding with spec snapshots and manifests
94
+ - app and workflow coding against existing Airlock specs
68
95
  - observe specs for controlled interface ingestion
69
96
  - orient specs for proposals, context, scoring, or exception queues
70
97
  - decision specs and action specs for governed follow-through
98
+ - OKF-style Markdown knowledge bundles for accepted agent context
71
99
  - artifact-grounded drafts from CSV, Excel, JSON, API docs, schemas, forms, screenshots, PDFs, or exports
72
100
  - airlock-specs library patterns as starting points, checked against current artifacts`;
73
101
  }
@@ -77,16 +105,23 @@ export function gettingStartedText(project) {
77
105
 
78
106
  Airlock MCP is the single installed interface for AI agents working with
79
107
  Airlock. It helps a person and their agent improve processes by designing
80
- specs, using specs for governed data movement, and planning OODA loops that
81
- can be assisted by people or agents.
108
+ specs, using specs for governed data movement, planning OODA loops, and building
109
+ apps or workflows that read from and submit through existing specs.
82
110
 
83
- Airlock MCP gives agents two kinds of Airlock expertise:
111
+ Airlock MCP gives agents four kinds of Airlock help:
84
112
 
85
113
  1. Spec design: draft, check, revise, import, clone, and prepare specs for
86
114
  installed Airlock validation.
87
115
  2. Airlock operating patterns: use specs to organize observations, orientation,
88
116
  governed decisions, controlled actions, separation of duties, and feedback
89
117
  loops.
118
+ 3. App and workflow implementation: build dashboards, queues, decision UIs,
119
+ analyses, and agent workflows that use existing specs without bypassing
120
+ Airlock contracts.
121
+ 4. Governance observation: use installed Airlock's read-only \`observe.*\`
122
+ procedures to inspect setup, access, activity, billing events, health,
123
+ context packets, and governance maps before deciding what an app or agent
124
+ should do.
90
125
 
91
126
  Start in a Git-backed specs repo such as ${specsRepoName(project)}. GitHub is
92
127
  the recommended default when the user has it set up, but any normal repository
@@ -98,7 +133,13 @@ Use this prompt in the specs repo:
98
133
 
99
134
  ${airlockPrompt(project)}
100
135
 
101
- First ask: What process do you want to improve?
136
+ First ask which delivery mode the user wants:
137
+
138
+ 1. Spec-first: design governed specs before building the app surface.
139
+ 2. App-first: build an app or workflow from existing specs.
140
+ 3. Co-development: develop the app and specs together.
141
+
142
+ For spec-first and co-development work, ask: What process do you want to improve?
102
143
 
103
144
  Airlock works best when we can identify the loop around a process:
104
145
 
@@ -122,8 +163,34 @@ points, patterns, and ideas. Library specs are not guaranteed to match the
122
163
  current shape of any third-party system. Prefer current API docs, real exports,
123
164
  samples, and other artifacts when they conflict with the library.
124
165
 
166
+ For governed Markdown knowledge, use the \`okf-knowledge-bundle\` pattern. It
167
+ creates a spec with \`core_config.payload_adapter\` set to
168
+ \`okf_knowledge_bundle\`. Installed Airlock loads locally validated bundles with
169
+ \`airlock.admin.load_okf_bundle(...)\`, can sync parsed metadata with
170
+ \`airlock.admin.sync_okf_bundle_metadata(...)\`, and exposes authoritative
171
+ accepted context through \`AIRLOCK_DATA.ACTIVE.V_OKF_CONCEPT_METADATA\`.
172
+
125
173
  Give Codex the messy version of the process. Airlock MCP should help turn it
126
- into a small first Airlock spec and a plan for more.`;
174
+ into a small first Airlock spec and a plan for more.
175
+
176
+ For app-first and co-development work, give Codex the app goal and available
177
+ specs. Airlock MCP should identify read specs, write specs, orienting views,
178
+ decision capture, and safe Airlock/Snowflake access paths. Installed Airlock
179
+ uses \`observe.*\` for read-only governance observation, \`agent.*\` for
180
+ governed agent work, and \`admin.*\` for administrative mutation. Start
181
+ read-side discovery with observe payloads such as \`observe.procedures\`,
182
+ \`observe.specs\`, \`observe.spec\`, \`observe.governance_map\`,
183
+ \`observe.explain_access\`, \`observe.health\`, \`observe.activity\`,
184
+ \`observe.admin_activity\`, \`observe.spec_admin_activity\`, and \`observe.billing_events\`;
185
+ for \`alter_spec\` activity, use \`CHANGED_SECTIONS\` and \`CHANGED_FIELDS\` to
186
+ triage what changed before fetching version snapshots. Do not use retired admin
187
+ read wrappers such as \`admin.list_specs\`, \`admin.describe_role\`, or
188
+ \`admin.list_events\`. It can
189
+ seed an app repo with \`airlock/specs.manifest.json\`, spec snapshots, sample
190
+ records, and generated helper folders. The app should submit decisions,
191
+ approvals, actions, comments, or follow-ups through Airlock spec contracts, not
192
+ direct table writes.
193
+ In co-development mode, keep the spec track and app track visible side by side.`;
127
194
  }
128
195
 
129
196
  export function helpText() {
@@ -148,5 +215,7 @@ Airlock MCP is the single installed interface for agents working with Airlock.
148
215
  Spec building is bundled inside that experience.
149
216
  Airlock operating patterns help connect specs into OODA loops, separation of
150
217
  duties, governed decisions, controlled actions, and feedback loops.
218
+ Airlock MCP can also help build apps and workflows that use existing specs for
219
+ approved reads and governed submissions.
151
220
  `;
152
221
  }
package/src/workbench.mjs CHANGED
@@ -23,6 +23,8 @@ const workspaceProperty = {
23
23
  maxLength: MAX_ARG_LENGTH,
24
24
  };
25
25
 
26
+ const appModeValues = ["spec-first", "app-first", "co-development"];
27
+
26
28
  export const WORKBENCH_TOOLS = [
27
29
  {
28
30
  name: "airlock_doctor",
@@ -42,6 +44,32 @@ export const WORKBENCH_TOOLS = [
42
44
  cwd: cwdProperty,
43
45
  }),
44
46
  },
47
+ {
48
+ name: "airlock_init_app_context",
49
+ description: "Seed an app repo with airlock/ spec snapshots, samples, generated placeholders, and a manifest.",
50
+ inputSchema: objectSchema({
51
+ path: {
52
+ type: "string",
53
+ description: "App repo path to initialize. Defaults to the current directory.",
54
+ maxLength: MAX_ARG_LENGTH,
55
+ },
56
+ mode: {
57
+ type: "string",
58
+ enum: appModeValues,
59
+ description: "Development mode. Defaults to app-first.",
60
+ },
61
+ specs: {
62
+ type: "array",
63
+ items: {
64
+ type: "string",
65
+ maxLength: MAX_ARG_LENGTH,
66
+ },
67
+ description: "Spec workspace directories or spec JSON files to snapshot.",
68
+ },
69
+ force: forceProperty,
70
+ cwd: cwdProperty,
71
+ }),
72
+ },
45
73
  {
46
74
  name: "airlock_list_patterns",
47
75
  description: "List bundled Airlock spec starter patterns.",
@@ -54,7 +82,7 @@ export const WORKBENCH_TOOLS = [
54
82
  {
55
83
  pattern: {
56
84
  type: "string",
57
- enum: ["blank", "posts"],
85
+ enum: ["blank", "okf-knowledge-bundle", "posts"],
58
86
  description: "Pattern to inspect.",
59
87
  },
60
88
  files: {
@@ -78,7 +106,7 @@ export const WORKBENCH_TOOLS = [
78
106
  },
79
107
  pattern: {
80
108
  type: "string",
81
- enum: ["blank", "posts"],
109
+ enum: ["blank", "okf-knowledge-bundle", "posts"],
82
110
  description: "Starting pattern. Defaults to blank.",
83
111
  },
84
112
  output: {
@@ -230,6 +258,28 @@ function optionalEnum(args, name, allowed, fallback) {
230
258
  return value;
231
259
  }
232
260
 
261
+ function optionalStringArray(args, name) {
262
+ const value = args?.[name];
263
+ if (value === undefined || value === null) {
264
+ return [];
265
+ }
266
+ if (!Array.isArray(value)) {
267
+ throw new Error(`${name} must be an array`);
268
+ }
269
+ return value.map((entry, index) => {
270
+ if (typeof entry !== "string") {
271
+ throw new Error(`${name}[${index}] must be a string`);
272
+ }
273
+ if (entry.length > MAX_ARG_LENGTH || /[\u0000-\u001f\u007f]/.test(entry)) {
274
+ throw new Error(`${name}[${index}] must be ${MAX_ARG_LENGTH} characters or fewer with no control characters`);
275
+ }
276
+ if (entry.startsWith("-")) {
277
+ throw new Error(`${name}[${index}] must not start with '-'`);
278
+ }
279
+ return entry;
280
+ });
281
+ }
282
+
233
283
  function requiredEnum(args, name, allowed) {
234
284
  const value = optionalEnum(args, name, allowed, undefined);
235
285
  if (value === undefined) {
@@ -264,6 +314,19 @@ function cliArgsForTool(name, args = {}) {
264
314
  if (force) cliArgs.push("--force");
265
315
  return { cwd, cliArgs };
266
316
  }
317
+ case "airlock_init_app_context": {
318
+ const cliArgs = [
319
+ "init-app-context",
320
+ optionalString(args, "path", "."),
321
+ "--mode",
322
+ optionalEnum(args, "mode", appModeValues, "app-first"),
323
+ ];
324
+ for (const spec of optionalStringArray(args, "specs")) {
325
+ cliArgs.push("--spec", spec);
326
+ }
327
+ if (force) cliArgs.push("--force");
328
+ return { cwd, cliArgs };
329
+ }
267
330
  case "airlock_list_patterns":
268
331
  return { cwd, cliArgs: ["list-patterns"] };
269
332
  case "airlock_show_pattern": {