@afora/mcp 0.0.0-stage → 0.0.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.
package/README.md CHANGED
@@ -1,3 +1,86 @@
1
- # Temporary Holding Version
1
+ # @afora/mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Repository declarations accept `deletion: "hard" | "soft"` (default `hard`), the actions `findMany`, `exists` and `findPage`, and a `provider` (`"inMemory"`, `{ use, options }` or `{ import }`); the application's provider is the top-level `provider` of the spec (`afora_spec_write`). An invalid provider is an issue (`unknown_provider`, `invalid_provider`, `invalid_import`) and nothing is written. `afora_extract` takes `imports` (`provider`, `repositories.<Name>.provider`) for a provider created without `providerOf`. The tools validate the setting and preserve soft deletion when generating or extracting an application.
4
+
5
+ An [MCP](https://modelcontextprotocol.io) server (stdio) that lets an agent build a domain with afora: discover the catalog, edit an `afora.json`, validate it, generate the code with [`@afora/codegen`](../codegen/README.md) and get the TypeScript diagnostics of what was generated. The spec it edits is described in the [afora.json guide](../../docs/afora-json.md).
6
+
7
+ The project needs `@afora/core` and TypeBox (`typebox: ^1`) as peer dependencies; install them with `bun add @afora/core typebox`.
8
+
9
+ ```bash
10
+ claude mcp add afora -- bunx @afora/mcp --project .
11
+ # or: afora-mcp --project <dir> [--app src/app.ts#App] [--providers afora.providers.ts] [--stages afora.stages.ts]
12
+ ```
13
+
14
+ `--project` is the project directory (the current one by default); every write stays inside it. `--app` names an `Application` (`file.ts#Export`) to extract the spec from when the project has no `afora.json`. Importing the application needs Bun on `PATH`. `--providers` is a module (a path from the project) exporting `providers`, or a default export: an array of `{ name, module, export }` that `provider.use` may name besides the built-in `inMemory`; `afora_catalog` lists them. `--stages` is the same for the stages an output's `pipe` may name (entries `{ name, module, export, kind? }`; `select` and `openapi` are built in and listed under `stages`).
15
+
16
+ ## Tools
17
+
18
+ | Tool | What it does |
19
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
20
+ | `afora_catalog` | What a spec can use, read from `@afora/core`: Std properties (flags and the `with` options each takes), record plugins, reserved keys, repository action rules, exception layers, core exceptions. |
21
+ | `afora_spec_read` | Reads `afora.json` with its issues; with no file and an `--app`, the spec extracted from that code (not written). |
22
+ | `afora_extract` | Extracts the spec of an `Application` of the project (a Bun subprocess imports it). Writes nothing. |
23
+ | `afora_spec_validate` | Validates a draft `spec`, or the file on disk; returns `{ valid, issues }`. |
24
+ | `afora_spec_upsert` | Adds or replaces one declaration (`kind` + `name` + `declaration`; kinds include `exception`, `useCase` (with steps), `command` and `output`); validates the whole spec and writes only if it is valid. |
25
+ | `afora_spec_remove` | Removes one declaration; writes only if the spec stays valid. |
26
+ | `afora_spec_write` | Replaces the whole spec (validated first). |
27
+ | `afora_generate` | `dryRun: true` returns the files. Otherwise writes them, then runs `bunx tsc --noEmit -p <project>` if there is a `tsconfig.json` and returns only the diagnostics of the generated files. |
28
+
29
+ `afora_extract` takes `imports` (`useCases.<Name>`, `useCases.<Name>.run.<name>`, `commands.<Name>.events`, `commands.<Name>.onEventError`, `outputs.<Name>.pipe.<index>`) for hand-written use cases, `run` functions, command handlers and stages. Resources: `afora://spec/schema` (JSON Schema of `afora.json`), `afora://catalog` and `afora://spec/guide` (a short guide for the agent: workflow, shape, rules). The `inputSchema` of each tool is JSON Schema derived from the same TypeBox schema as the spec.
30
+
31
+ ## Expose an output as MCP tools
32
+
33
+ `mcpTools` is a sink stage of an afora output: it turns the operations of the output into MCP tools, so an agent can call your domain directly (the same operations `@afora/hono` serves over HTTP).
34
+
35
+ ```ts
36
+ import { outputOf } from "@afora/core";
37
+ import { mcpTools } from "@afora/mcp";
38
+
39
+ const Tools = outputOf({
40
+ name: "Blog",
41
+ commands: [Users, Posts],
42
+ statuses: { Unauthorized: 401 },
43
+ pipe: [
44
+ mcpTools({
45
+ name: "blog",
46
+ version: "1.0.0",
47
+ prefix: "blog_",
48
+ context: { authorization: "authorization" },
49
+ }),
50
+ ],
51
+ });
52
+
53
+ const mcp = Tools.build(); // { tools, call, register, server }
54
+ await mcp.server().connect(transport); // any transport of the MCP SDK
55
+ ```
56
+
57
+ - Options (JSON, so the stage can live in `afora.json`): `name`, `version` (of the server `server()` creates), `prefix`, `include` (operation ids; all by default) and `context`.
58
+ - One tool per operation, named `{prefix}{command}_{operation}` in snake case (`blog_users_register`). The description is the operation one (plus `Deprecated.`) and `inputSchema` is the JSON Schema of the input, inline, with no `$ref`.
59
+ - A call runs `operation.invoke(args, { context })` and answers one text content with the response as JSON; sensitive values are masked.
60
+ - Errors answer `isError: true` with the RFC 9457 problem as JSON text; its `status` comes from `statusOf` (the output `statuses` first). Invalid input is a 400 with `issues`, an unknown tool a 404.
61
+ - Context: `context: { token: "authorization" }` fills `context.token` from the value named `authorization` in `extra.values` of `call(name, args, extra)`. With `register`/`server()`, the values are the HTTP headers of the request (lower-case) and the string entries of the request `_meta`. Anything richer, such as authentication, is a `transform` stage that wraps `invoke`.
62
+ - The result is `{ tools, call(name, args, extra?), register(server), server() }`: `register` installs `tools/list` and `tools/call` on a low-level SDK `Server`, `server()` returns a new one with the `tools` capability.
63
+ - For codegen, `stages` and `McpStages` are exported (`--stages @afora/mcp`): `{ name: "mcpTools", module: "@afora/mcp", export: "mcpTools", kind: "sink" }`.
64
+
65
+ ## Safety
66
+
67
+ - Writes are confined to the project: paths are resolved, `..` and symlinks that leave the project are rejected (also for `afora.json` itself and for `output.dir`).
68
+ - A generated file is overwritten or removed only if it starts with the banner `// Generated by @afora/codegen from afora.json. Do not edit.`. If any file would be refused (it exists without the banner) nothing is written.
69
+ - Nothing else is touched. An invalid spec is never written.
70
+
71
+ ## Programmatic use
72
+
73
+ ```ts
74
+ import { createServer } from "@afora/mcp";
75
+
76
+ const server = await createServer({
77
+ project: "/path/to/project",
78
+ stages: [
79
+ { name: "hono", module: "@afora/hono", export: "hono", kind: "sink" },
80
+ ],
81
+ providers: [
82
+ { name: "drizzle", module: "@acme/drizzle", export: "drizzleProvider" },
83
+ ],
84
+ });
85
+ await server.connect(transport); // any transport of the MCP SDK
86
+ ```
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ /** Starts the server on stdio. */
3
+ export declare function main(argv: string[]): Promise<number>;