@axonity-ai/mcp 0.1.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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/client.d.ts +24 -0
- package/dist/client.js +70 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +35 -0
- package/dist/errors.js +62 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +97 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/conventions.d.ts +11 -0
- package/dist/tools/conventions.js +52 -0
- package/dist/tools/conventions.js.map +1 -0
- package/dist/tools/extras.d.ts +14 -0
- package/dist/tools/extras.js +81 -0
- package/dist/tools/extras.js.map +1 -0
- package/dist/tools/register.d.ts +30 -0
- package/dist/tools/register.js +66 -0
- package/dist/tools/register.js.map +1 -0
- package/dist/tools/result.d.ts +21 -0
- package/dist/tools/result.js +30 -0
- package/dist/tools/result.js.map +1 -0
- package/dist/tools/workflowMutations.d.ts +11 -0
- package/dist/tools/workflowMutations.js +45 -0
- package/dist/tools/workflowMutations.js.map +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Axonity
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @axonity-ai/mcp — Axonity Flow MCP connector
|
|
2
|
+
|
|
3
|
+
A local [Model Context Protocol](https://modelcontextprotocol.io) server that lets
|
|
4
|
+
an external agent (e.g. **Claude Code** on your laptop) read, draft, and update
|
|
5
|
+
**workflows, agents, tools, skills, policies and reference docs** in your Axonity
|
|
6
|
+
tenant — the same verbs the internal Builder team has, minus direct publish.
|
|
7
|
+
|
|
8
|
+
It runs on your machine and talks to Axonity **only over the public REST API**,
|
|
9
|
+
authenticated with a per-tenant **service token**. The backend re-enforces
|
|
10
|
+
tenant + scope on every call, so the connector is not a trust boundary.
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
1. **Mint a service token** in Axonity → **Settings → API tokens**. Copy it once
|
|
15
|
+
(it starts with `axs_`); you won't see it again. Use a **read-only** token if
|
|
16
|
+
you only want the agent to read.
|
|
17
|
+
2. **Add the connector to Claude Code:**
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
claude mcp add axonity \
|
|
21
|
+
--env AXONITY_TOKEN=axs_your_token_here \
|
|
22
|
+
--env AXONITY_API_URL=https://app.axonity.ai \
|
|
23
|
+
-- npx -y @axonity-ai/mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`AXONITY_API_URL` is optional (defaults to the Axonity SaaS URL); set it if you
|
|
27
|
+
self-host.
|
|
28
|
+
|
|
29
|
+
3. Ask Claude Code things like *"list my Axonity workflows"*, *"create a workflow
|
|
30
|
+
called Onboarding"*, or *"add a step to workflow X"*.
|
|
31
|
+
|
|
32
|
+
## Tools
|
|
33
|
+
|
|
34
|
+
| Tool | What it does |
|
|
35
|
+
|------|--------------|
|
|
36
|
+
| `axonity_conventions` | The authoring rules (drafts vs live, optimistic locking, per-entity fields). Read this first. |
|
|
37
|
+
Each entity — **workflow, agent, tool, skill, policy, reference_doc** — gets the
|
|
38
|
+
same five verbs:
|
|
39
|
+
|
|
40
|
+
| Tool (per `<entity>`) | What it does |
|
|
41
|
+
|------|--------------|
|
|
42
|
+
| `list_<entity>s` | List the tenant's entities. |
|
|
43
|
+
| `read_<entity>` | Read one by id (incl. its version — read before you update). |
|
|
44
|
+
| `create_<entity>` | Create a new **draft**. |
|
|
45
|
+
| `update_<entity>` | Update a draft (carries `expectedVersion`; 409 on a stale write). |
|
|
46
|
+
| `request_publish_<entity>` | Ask for a draft to be published — creates a pending approval; never publishes. |
|
|
47
|
+
|
|
48
|
+
Plus `apply_workflow_mutations` for structural workflow edits (add steps, connect
|
|
49
|
+
edges) via mutation commands.
|
|
50
|
+
|
|
51
|
+
Also:
|
|
52
|
+
|
|
53
|
+
- **Personas** (agent-scoped, 1:1 with an agent): `read_agent_persona`,
|
|
54
|
+
`create_agent_persona`, `update_persona`.
|
|
55
|
+
- **Connectors** (a tool of type `connector`): `create_connector`,
|
|
56
|
+
`update_connector` — `authConfig` must be placeholders only; a human fills real
|
|
57
|
+
secrets in Axonity.
|
|
58
|
+
- **Attach memory to a target**: `attach_skill_to_agent`,
|
|
59
|
+
`attach_skill_to_workflow`, `attach_policy_to_agent`, `attach_reference_to_agent`.
|
|
60
|
+
|
|
61
|
+
## Publishing
|
|
62
|
+
|
|
63
|
+
The connector **never publishes directly**. `request_publish_*` creates a
|
|
64
|
+
**pending approval** in Axonity; an admin approves it there, and only then does
|
|
65
|
+
the draft go live. Rejecting it changes nothing.
|
|
66
|
+
|
|
67
|
+
## Development
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install
|
|
71
|
+
npm run typecheck
|
|
72
|
+
npm test
|
|
73
|
+
npm run build # emits dist/
|
|
74
|
+
```
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin HTTP client for the Axonity REST API.
|
|
3
|
+
*
|
|
4
|
+
* The connector talks to Axonity ONLY over the public REST API — no direct DB,
|
|
5
|
+
* no backend internals. Every request carries the service token as a bearer;
|
|
6
|
+
* the backend re-enforces tenant + scope, so this client is not a trust
|
|
7
|
+
* boundary. Failures are mapped to typed errors (see errors.ts).
|
|
8
|
+
*/
|
|
9
|
+
import type { AxonityConfig } from "./config.js";
|
|
10
|
+
export type Json = Record<string, unknown>;
|
|
11
|
+
export declare class AxonityClient {
|
|
12
|
+
private readonly config;
|
|
13
|
+
constructor(config: AxonityConfig);
|
|
14
|
+
private url;
|
|
15
|
+
/**
|
|
16
|
+
* Issue a request and return the parsed JSON body. Non-2xx responses are
|
|
17
|
+
* thrown as typed errors; a 204/empty body resolves to `undefined`.
|
|
18
|
+
*/
|
|
19
|
+
request<T = unknown>(method: string, path: string, body?: unknown): Promise<T>;
|
|
20
|
+
get<T = unknown>(path: string): Promise<T>;
|
|
21
|
+
post<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
22
|
+
put<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
23
|
+
patch<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
24
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin HTTP client for the Axonity REST API.
|
|
3
|
+
*
|
|
4
|
+
* The connector talks to Axonity ONLY over the public REST API — no direct DB,
|
|
5
|
+
* no backend internals. Every request carries the service token as a bearer;
|
|
6
|
+
* the backend re-enforces tenant + scope, so this client is not a trust
|
|
7
|
+
* boundary. Failures are mapped to typed errors (see errors.ts).
|
|
8
|
+
*/
|
|
9
|
+
import { AxonityApiError, errorForStatus } from "./errors.js";
|
|
10
|
+
export class AxonityClient {
|
|
11
|
+
config;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config;
|
|
14
|
+
}
|
|
15
|
+
url(path) {
|
|
16
|
+
return `${this.config.apiUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Issue a request and return the parsed JSON body. Non-2xx responses are
|
|
20
|
+
* thrown as typed errors; a 204/empty body resolves to `undefined`.
|
|
21
|
+
*/
|
|
22
|
+
async request(method, path, body) {
|
|
23
|
+
let resp;
|
|
24
|
+
try {
|
|
25
|
+
resp = await fetch(this.url(path), {
|
|
26
|
+
method,
|
|
27
|
+
headers: {
|
|
28
|
+
Authorization: `Bearer ${this.config.token}`,
|
|
29
|
+
...(body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
30
|
+
},
|
|
31
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
catch (cause) {
|
|
35
|
+
// Network-level failure (DNS, connection refused, TLS) — not an HTTP status.
|
|
36
|
+
throw new AxonityApiError(`Could not reach the Axonity API at ${this.config.apiUrl}. ` +
|
|
37
|
+
"Check AXONITY_API_URL and your network.", 0, cause instanceof Error ? cause.message : String(cause));
|
|
38
|
+
}
|
|
39
|
+
const text = await resp.text();
|
|
40
|
+
const parsed = text ? safeJson(text) : undefined;
|
|
41
|
+
if (!resp.ok) {
|
|
42
|
+
const detail = parsed && typeof parsed === "object" && "detail" in parsed
|
|
43
|
+
? parsed.detail
|
|
44
|
+
: parsed;
|
|
45
|
+
throw errorForStatus(resp.status, detail);
|
|
46
|
+
}
|
|
47
|
+
return parsed;
|
|
48
|
+
}
|
|
49
|
+
get(path) {
|
|
50
|
+
return this.request("GET", path);
|
|
51
|
+
}
|
|
52
|
+
post(path, body) {
|
|
53
|
+
return this.request("POST", path, body);
|
|
54
|
+
}
|
|
55
|
+
put(path, body) {
|
|
56
|
+
return this.request("PUT", path, body);
|
|
57
|
+
}
|
|
58
|
+
patch(path, body) {
|
|
59
|
+
return this.request("PATCH", path, body);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function safeJson(text) {
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(text);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return text;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI9D,MAAM,OAAO,aAAa;IACK;IAA7B,YAA6B,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAE9C,GAAG,CAAC,IAAY;QACtB,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;IAC5E,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CACX,MAAc,EACd,IAAY,EACZ,IAAc;QAEd,IAAI,IAAc,CAAC;QACnB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;gBACjC,MAAM;gBACN,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;oBAC5C,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACtE;gBACD,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;aAC5D,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,6EAA6E;YAC7E,MAAM,IAAI,eAAe,CACvB,sCAAsC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI;gBAC1D,yCAAyC,EAC3C,CAAC,EACD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAEjD,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,MAAM,MAAM,GACV,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,QAAQ,IAAI,MAAM;gBACxD,CAAC,CAAE,MAA8B,CAAC,MAAM;gBACxC,CAAC,CAAC,MAAM,CAAC;YACb,MAAM,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,MAAW,CAAC;IACrB,CAAC;IAED,GAAG,CAAc,IAAY;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAI,KAAK,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,CAAc,IAAY,EAAE,IAAc;QAC5C,OAAO,IAAI,CAAC,OAAO,CAAI,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,GAAG,CAAc,IAAY,EAAE,IAAc;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAI,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAc,IAAY,EAAE,IAAc;QAC7C,OAAO,IAAI,CAAC,OAAO,CAAI,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;CACF;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime configuration for the Axonity MCP connector.
|
|
3
|
+
*
|
|
4
|
+
* The connector authenticates to Axonity with a per-tenant SERVICE TOKEN
|
|
5
|
+
* (minted in Axonity → Settings → API tokens). The token fixes the tenant; the
|
|
6
|
+
* connector never chooses one. Values come from the environment so the token
|
|
7
|
+
* never lands in a config file the agent can read.
|
|
8
|
+
*/
|
|
9
|
+
export interface AxonityConfig {
|
|
10
|
+
/** Base URL of the Axonity API, e.g. https://app.axonity.ai (no trailing slash). */
|
|
11
|
+
apiUrl: string;
|
|
12
|
+
/** The service token (`axs_…`) presented as a bearer on every request. */
|
|
13
|
+
token: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Read configuration from the environment.
|
|
17
|
+
*
|
|
18
|
+
* - `AXONITY_TOKEN` (required): the `axs_…` service token.
|
|
19
|
+
* - `AXONITY_API_URL` (optional): defaults to the Axonity SaaS URL.
|
|
20
|
+
*
|
|
21
|
+
* Throws if the token is missing so the process fails fast at startup with a
|
|
22
|
+
* clear message rather than 401-ing on the first tool call.
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadConfig(env?: NodeJS.ProcessEnv): AxonityConfig;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime configuration for the Axonity MCP connector.
|
|
3
|
+
*
|
|
4
|
+
* The connector authenticates to Axonity with a per-tenant SERVICE TOKEN
|
|
5
|
+
* (minted in Axonity → Settings → API tokens). The token fixes the tenant; the
|
|
6
|
+
* connector never chooses one. Values come from the environment so the token
|
|
7
|
+
* never lands in a config file the agent can read.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_API_URL = "https://app.axonity.ai";
|
|
10
|
+
/**
|
|
11
|
+
* Read configuration from the environment.
|
|
12
|
+
*
|
|
13
|
+
* - `AXONITY_TOKEN` (required): the `axs_…` service token.
|
|
14
|
+
* - `AXONITY_API_URL` (optional): defaults to the Axonity SaaS URL.
|
|
15
|
+
*
|
|
16
|
+
* Throws if the token is missing so the process fails fast at startup with a
|
|
17
|
+
* clear message rather than 401-ing on the first tool call.
|
|
18
|
+
*/
|
|
19
|
+
export function loadConfig(env = process.env) {
|
|
20
|
+
const token = env.AXONITY_TOKEN?.trim();
|
|
21
|
+
if (!token) {
|
|
22
|
+
throw new Error("AXONITY_TOKEN is not set. Mint a service token in Axonity " +
|
|
23
|
+
"(Settings → API tokens) and pass it as the AXONITY_TOKEN environment variable.");
|
|
24
|
+
}
|
|
25
|
+
const apiUrl = (env.AXONITY_API_URL?.trim() || DEFAULT_API_URL).replace(/\/+$/, "");
|
|
26
|
+
return { apiUrl, token };
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAEjD;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,4DAA4D;YAC1D,gFAAgF,CACnF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,eAAe,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACpF,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC3B,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed errors for the Axonity MCP connector.
|
|
3
|
+
*
|
|
4
|
+
* Every REST failure is mapped to one of these so tool handlers can return a
|
|
5
|
+
* clean, actionable message to the agent — never a raw stack trace or an
|
|
6
|
+
* opaque status code.
|
|
7
|
+
*/
|
|
8
|
+
export declare class AxonityApiError extends Error {
|
|
9
|
+
/** HTTP status that produced this error. */
|
|
10
|
+
readonly status: number;
|
|
11
|
+
/** Server-provided detail, when present. */
|
|
12
|
+
readonly detail?: unknown | undefined;
|
|
13
|
+
constructor(message: string,
|
|
14
|
+
/** HTTP status that produced this error. */
|
|
15
|
+
status: number,
|
|
16
|
+
/** Server-provided detail, when present. */
|
|
17
|
+
detail?: unknown | undefined);
|
|
18
|
+
}
|
|
19
|
+
/** 409 — the entity was modified since the agent read it. Re-read and retry. */
|
|
20
|
+
export declare class VersionConflictError extends AxonityApiError {
|
|
21
|
+
constructor(detail?: unknown);
|
|
22
|
+
}
|
|
23
|
+
/** 401 — the service token is missing, unknown, or revoked. */
|
|
24
|
+
export declare class AuthError extends AxonityApiError {
|
|
25
|
+
constructor(detail?: unknown);
|
|
26
|
+
}
|
|
27
|
+
/** 403 — the token lacks the scope for this action (e.g. read-only on a write). */
|
|
28
|
+
export declare class ForbiddenError extends AxonityApiError {
|
|
29
|
+
constructor(detail?: unknown);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Map an HTTP response to the right error type. `detail` is the parsed body
|
|
33
|
+
* (FastAPI returns `{ detail: … }`), passed through for context.
|
|
34
|
+
*/
|
|
35
|
+
export declare function errorForStatus(status: number, detail?: unknown): AxonityApiError;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed errors for the Axonity MCP connector.
|
|
3
|
+
*
|
|
4
|
+
* Every REST failure is mapped to one of these so tool handlers can return a
|
|
5
|
+
* clean, actionable message to the agent — never a raw stack trace or an
|
|
6
|
+
* opaque status code.
|
|
7
|
+
*/
|
|
8
|
+
export class AxonityApiError extends Error {
|
|
9
|
+
status;
|
|
10
|
+
detail;
|
|
11
|
+
constructor(message,
|
|
12
|
+
/** HTTP status that produced this error. */
|
|
13
|
+
status,
|
|
14
|
+
/** Server-provided detail, when present. */
|
|
15
|
+
detail) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.status = status;
|
|
18
|
+
this.detail = detail;
|
|
19
|
+
this.name = "AxonityApiError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** 409 — the entity was modified since the agent read it. Re-read and retry. */
|
|
23
|
+
export class VersionConflictError extends AxonityApiError {
|
|
24
|
+
constructor(detail) {
|
|
25
|
+
super("Version conflict: the entity changed since you read it. Read it again to " +
|
|
26
|
+
"get the current version, then re-apply your change.", 409, detail);
|
|
27
|
+
this.name = "VersionConflictError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** 401 — the service token is missing, unknown, or revoked. */
|
|
31
|
+
export class AuthError extends AxonityApiError {
|
|
32
|
+
constructor(detail) {
|
|
33
|
+
super("Authentication failed (401): the service token is missing, invalid, or revoked. " +
|
|
34
|
+
"Check AXONITY_TOKEN, or mint a new token in Axonity → Settings → API tokens.", 401, detail);
|
|
35
|
+
this.name = "AuthError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** 403 — the token lacks the scope for this action (e.g. read-only on a write). */
|
|
39
|
+
export class ForbiddenError extends AxonityApiError {
|
|
40
|
+
constructor(detail) {
|
|
41
|
+
super("Forbidden (403): this service token is not allowed to perform that action. " +
|
|
42
|
+
"A read-only token cannot create, update, or request-publish.", 403, detail);
|
|
43
|
+
this.name = "ForbiddenError";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Map an HTTP response to the right error type. `detail` is the parsed body
|
|
48
|
+
* (FastAPI returns `{ detail: … }`), passed through for context.
|
|
49
|
+
*/
|
|
50
|
+
export function errorForStatus(status, detail) {
|
|
51
|
+
switch (status) {
|
|
52
|
+
case 401:
|
|
53
|
+
return new AuthError(detail);
|
|
54
|
+
case 403:
|
|
55
|
+
return new ForbiddenError(detail);
|
|
56
|
+
case 409:
|
|
57
|
+
return new VersionConflictError(detail);
|
|
58
|
+
default:
|
|
59
|
+
return new AxonityApiError(`Axonity API request failed (${status}).`, status, detail);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAI7B;IAEA;IALX,YACE,OAAe;IACf,4CAA4C;IACnC,MAAc;IACvB,4CAA4C;IACnC,MAAgB;QAEzB,KAAK,CAAC,OAAO,CAAC,CAAC;QAJN,WAAM,GAAN,MAAM,CAAQ;QAEd,WAAM,GAAN,MAAM,CAAU;QAGzB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,gFAAgF;AAChF,MAAM,OAAO,oBAAqB,SAAQ,eAAe;IACvD,YAAY,MAAgB;QAC1B,KAAK,CACH,2EAA2E;YACzE,qDAAqD,EACvD,GAAG,EACH,MAAM,CACP,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED,+DAA+D;AAC/D,MAAM,OAAO,SAAU,SAAQ,eAAe;IAC5C,YAAY,MAAgB;QAC1B,KAAK,CACH,kFAAkF;YAChF,8EAA8E,EAChF,GAAG,EACH,MAAM,CACP,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,mFAAmF;AACnF,MAAM,OAAO,cAAe,SAAQ,eAAe;IACjD,YAAY,MAAgB;QAC1B,KAAK,CACH,6EAA6E;YAC3E,8DAA8D,EAChE,GAAG,EACH,MAAM,CACP,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,MAAgB;IAC7D,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,GAAG;YACN,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;QAC/B,KAAK,GAAG;YACN,OAAO,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;QACpC,KAAK,GAAG;YACN,OAAO,IAAI,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAC1C;YACE,OAAO,IAAI,eAAe,CACxB,+BAA+B,MAAM,IAAI,EACzC,MAAM,EACN,MAAM,CACP,CAAC;IACN,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Axonity Flow — MCP connector (epic #652 / C4).
|
|
4
|
+
*
|
|
5
|
+
* A local stdio MCP server that lets an external agent (e.g. Claude Code) read,
|
|
6
|
+
* draft, and update workflows, agents and tools in an Axonity tenant. Add it to
|
|
7
|
+
* your client with:
|
|
8
|
+
*
|
|
9
|
+
* claude mcp add axonity -- npx -y @axonity-ai/mcp
|
|
10
|
+
*
|
|
11
|
+
* with AXONITY_TOKEN (a service token minted in Axonity → Settings → API tokens)
|
|
12
|
+
* and, if self-hosting, AXONITY_API_URL in the environment.
|
|
13
|
+
*
|
|
14
|
+
* It talks to Axonity ONLY over the public REST API; the backend re-enforces
|
|
15
|
+
* tenant + scope on every call. Publishing is human-approved and lands in a
|
|
16
|
+
* later slice (C3) — this connector never publishes directly.
|
|
17
|
+
*/
|
|
18
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
+
import { AxonityClient } from "./client.js";
|
|
20
|
+
export declare function buildServer(client: AxonityClient): McpServer;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Axonity Flow — MCP connector (epic #652 / C4).
|
|
4
|
+
*
|
|
5
|
+
* A local stdio MCP server that lets an external agent (e.g. Claude Code) read,
|
|
6
|
+
* draft, and update workflows, agents and tools in an Axonity tenant. Add it to
|
|
7
|
+
* your client with:
|
|
8
|
+
*
|
|
9
|
+
* claude mcp add axonity -- npx -y @axonity-ai/mcp
|
|
10
|
+
*
|
|
11
|
+
* with AXONITY_TOKEN (a service token minted in Axonity → Settings → API tokens)
|
|
12
|
+
* and, if self-hosting, AXONITY_API_URL in the environment.
|
|
13
|
+
*
|
|
14
|
+
* It talks to Axonity ONLY over the public REST API; the backend re-enforces
|
|
15
|
+
* tenant + scope on every call. Publishing is human-approved and lands in a
|
|
16
|
+
* later slice (C3) — this connector never publishes directly.
|
|
17
|
+
*/
|
|
18
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
20
|
+
import { AxonityClient } from "./client.js";
|
|
21
|
+
import { loadConfig } from "./config.js";
|
|
22
|
+
import { registerConventions } from "./tools/conventions.js";
|
|
23
|
+
import { registerAttachTools, registerConnectorTools, registerPersonaTools, } from "./tools/extras.js";
|
|
24
|
+
import { registerEntityTools } from "./tools/register.js";
|
|
25
|
+
import { registerWorkflowMutations } from "./tools/workflowMutations.js";
|
|
26
|
+
/**
|
|
27
|
+
* The entities the connector covers. Core entities (C4) plus memory
|
|
28
|
+
* entities (C5) — skills, policies, reference docs — which have the same
|
|
29
|
+
* draft→publish lifecycle via #443's unified versioning.
|
|
30
|
+
*/
|
|
31
|
+
const ENTITIES = [
|
|
32
|
+
{
|
|
33
|
+
singular: "workflow",
|
|
34
|
+
basePath: "/api/v1/workflows",
|
|
35
|
+
updateMethod: "PATCH",
|
|
36
|
+
label: "workflows (business processes)",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
singular: "agent",
|
|
40
|
+
basePath: "/api/v1/agents",
|
|
41
|
+
updateMethod: "PUT",
|
|
42
|
+
label: "agents",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
singular: "tool",
|
|
46
|
+
basePath: "/api/v1/tools",
|
|
47
|
+
updateMethod: "PUT",
|
|
48
|
+
label: "tools (functions, connectors, validators, evaluators)",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
singular: "skill",
|
|
52
|
+
basePath: "/api/v1/skills",
|
|
53
|
+
updateMethod: "PUT",
|
|
54
|
+
label: "skills (reusable know-how for agents)",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
singular: "policy",
|
|
58
|
+
basePath: "/api/v1/policies",
|
|
59
|
+
updateMethod: "PUT",
|
|
60
|
+
label: "policies (rules and guardrails for agents)",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
singular: "reference_doc",
|
|
64
|
+
basePath: "/api/v1/reference-docs",
|
|
65
|
+
updateMethod: "PUT",
|
|
66
|
+
label: "reference docs (background knowledge for agents)",
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
export function buildServer(client) {
|
|
70
|
+
const server = new McpServer({ name: "axonity", version: "0.1.0" });
|
|
71
|
+
registerConventions(server);
|
|
72
|
+
for (const def of ENTITIES) {
|
|
73
|
+
registerEntityTools(server, client, def);
|
|
74
|
+
}
|
|
75
|
+
registerWorkflowMutations(server, client);
|
|
76
|
+
registerPersonaTools(server, client);
|
|
77
|
+
registerConnectorTools(server, client);
|
|
78
|
+
registerAttachTools(server, client);
|
|
79
|
+
return server;
|
|
80
|
+
}
|
|
81
|
+
async function main() {
|
|
82
|
+
const config = loadConfig();
|
|
83
|
+
const client = new AxonityClient(config);
|
|
84
|
+
const server = buildServer(client);
|
|
85
|
+
const transport = new StdioServerTransport();
|
|
86
|
+
await server.connect(transport);
|
|
87
|
+
// Log to stderr — stdout is the MCP channel and must stay clean.
|
|
88
|
+
console.error(`Axonity MCP connector ready (${config.apiUrl}).`);
|
|
89
|
+
}
|
|
90
|
+
// Only run when executed directly (not when imported by tests).
|
|
91
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
92
|
+
main().catch((err) => {
|
|
93
|
+
console.error("Axonity MCP connector failed to start:", err);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# 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,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `axonity_conventions` guidance tool.
|
|
3
|
+
*
|
|
4
|
+
* An external agent needs the same authoring rules the internal Builder team
|
|
5
|
+
* carries, or its drafts will be low quality. This is a static conventions
|
|
6
|
+
* reference for the MLP; a single-sourced `discover_<entity>_operations` tool
|
|
7
|
+
* backed by the platform's own discovery contract is a planned refinement
|
|
8
|
+
* (epic #652 C4 — "prefer a thin backend read endpoint").
|
|
9
|
+
*/
|
|
10
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
|
+
export declare function registerConventions(server: McpServer): void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `axonity_conventions` guidance tool.
|
|
3
|
+
*
|
|
4
|
+
* An external agent needs the same authoring rules the internal Builder team
|
|
5
|
+
* carries, or its drafts will be low quality. This is a static conventions
|
|
6
|
+
* reference for the MLP; a single-sourced `discover_<entity>_operations` tool
|
|
7
|
+
* backed by the platform's own discovery contract is a planned refinement
|
|
8
|
+
* (epic #652 C4 — "prefer a thin backend read endpoint").
|
|
9
|
+
*/
|
|
10
|
+
const CONVENTIONS = `# Authoring conventions for Axonity entities
|
|
11
|
+
|
|
12
|
+
Read this before creating or updating anything.
|
|
13
|
+
|
|
14
|
+
## Drafts, not live
|
|
15
|
+
- \`create_*\` and \`update_*\` produce a DRAFT. The live runtime is unaffected
|
|
16
|
+
until a human publishes it in Axonity. You cannot publish directly.
|
|
17
|
+
- \`request_publish_*\` does NOT publish — it creates a pending approval a human
|
|
18
|
+
approves in Axonity. Use it when a draft is ready; then tell the user to
|
|
19
|
+
approve it in their Axonity approvals inbox.
|
|
20
|
+
|
|
21
|
+
## Optimistic locking (always read-then-write)
|
|
22
|
+
- Before \`update_*\`, call \`read_*\` and use the \`version\` you get as
|
|
23
|
+
\`expectedVersion\`. If the update returns a 409 conflict, someone else changed
|
|
24
|
+
it — read again and re-apply your change onto the new version.
|
|
25
|
+
|
|
26
|
+
## Field shape
|
|
27
|
+
- All fields are camelCase JSON (e.g. \`capabilityTier\`, not \`capability_tier\`).
|
|
28
|
+
- \`update_*\` is a partial change for agents/tools and a partial patch for
|
|
29
|
+
workflows: send only the fields you are changing.
|
|
30
|
+
- The backend validates fields; a 422 error tells you exactly which field is wrong.
|
|
31
|
+
|
|
32
|
+
## Per entity
|
|
33
|
+
- **workflow**: \`create_workflow\` makes a stub (name, description). Build its
|
|
34
|
+
steps/edges with \`apply_workflow_mutations\` (a list of mutation commands) —
|
|
35
|
+
the workflow document is edited through commands, never by replacing it wholesale.
|
|
36
|
+
- **agent**: fields include name, avatar, capabilityTier, creativityTier,
|
|
37
|
+
learningMode, permissions, delegationTargetIds, toolIds, systemToolIds, status.
|
|
38
|
+
- **tool**: fields include name, description, type
|
|
39
|
+
("function" | "connector" | "validator" | "evaluator"), inputSchema,
|
|
40
|
+
outputSchema, implementation, authConfig, status. For a connector, set
|
|
41
|
+
\`type: "connector"\`; NEVER put real credentials in authConfig — use
|
|
42
|
+
placeholders and let a human fill secrets in the Axonity tool editor.
|
|
43
|
+
|
|
44
|
+
## Tenant
|
|
45
|
+
- Your token fixes the tenant. You cannot act across tenants; never send a
|
|
46
|
+
tenant id in a body.
|
|
47
|
+
`;
|
|
48
|
+
export function registerConventions(server) {
|
|
49
|
+
server.tool("axonity_conventions", "Read the authoring conventions (drafts vs live, optimistic locking, field " +
|
|
50
|
+
"shapes per entity). Call this before creating or updating entities.", {}, async () => ({ content: [{ type: "text", text: CONVENTIONS }] }));
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=conventions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conventions.js","sourceRoot":"","sources":["../../src/tools/conventions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCnB,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,MAAiB;IACnD,MAAM,CAAC,IAAI,CACT,qBAAqB,EACrB,4EAA4E;QAC1E,qEAAqE,EACvE,EAAE,EACF,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAC1E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secondary MCP tools (epic #652 C5): personas (agent-scoped), connectors
|
|
3
|
+
* (a tool subtype), and attaching memory to a target.
|
|
4
|
+
*
|
|
5
|
+
* These use routes with shapes that don't fit the generic entity registrar:
|
|
6
|
+
* personas are 1:1 with an agent (no standalone list), connectors are a tool
|
|
7
|
+
* with `type: "connector"` and MUST NOT carry real credentials, and attach is a
|
|
8
|
+
* path-only link.
|
|
9
|
+
*/
|
|
10
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
|
+
import type { AxonityClient } from "../client.js";
|
|
12
|
+
export declare function registerPersonaTools(server: McpServer, client: AxonityClient): void;
|
|
13
|
+
export declare function registerConnectorTools(server: McpServer, client: AxonityClient): void;
|
|
14
|
+
export declare function registerAttachTools(server: McpServer, client: AxonityClient): void;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secondary MCP tools (epic #652 C5): personas (agent-scoped), connectors
|
|
3
|
+
* (a tool subtype), and attaching memory to a target.
|
|
4
|
+
*
|
|
5
|
+
* These use routes with shapes that don't fit the generic entity registrar:
|
|
6
|
+
* personas are 1:1 with an agent (no standalone list), connectors are a tool
|
|
7
|
+
* with `type: "connector"` and MUST NOT carry real credentials, and attach is a
|
|
8
|
+
* path-only link.
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { guard, jsonResult } from "./result.js";
|
|
12
|
+
/** A value is a safe placeholder if it's empty or a `{{ … }}` template. */
|
|
13
|
+
function isPlaceholder(value) {
|
|
14
|
+
if (value === "" || value === null || value === undefined)
|
|
15
|
+
return true;
|
|
16
|
+
return typeof value === "string" && /^\s*\{\{.*\}\}\s*$/.test(value);
|
|
17
|
+
}
|
|
18
|
+
export function registerPersonaTools(server, client) {
|
|
19
|
+
server.tool("read_agent_persona", "Read an agent's persona (voice/character). A persona is 1:1 with an agent — " +
|
|
20
|
+
"there is no standalone persona list.", { agentId: z.string().describe("The agent's id.") }, async ({ agentId }) => guard(async () => jsonResult(await client.get(`/api/v1/agents/${agentId}/persona`))));
|
|
21
|
+
server.tool("create_agent_persona", "Create an agent's persona. Pass name, characterText and optional status.", {
|
|
22
|
+
agentId: z.string().describe("The agent's id."),
|
|
23
|
+
fields: z
|
|
24
|
+
.record(z.unknown())
|
|
25
|
+
.describe('Persona fields, e.g. { "name": "…", "characterText": "…" }.'),
|
|
26
|
+
}, async ({ agentId, fields }) => guard(async () => jsonResult(await client.post(`/api/v1/agents/${agentId}/persona`, fields))));
|
|
27
|
+
server.tool("update_persona", "Update a persona by its id (read the agent's persona first for its version).", {
|
|
28
|
+
personaId: z.string().describe("The persona's id."),
|
|
29
|
+
expectedVersion: z
|
|
30
|
+
.number()
|
|
31
|
+
.int()
|
|
32
|
+
.describe("The version you last read — 409 if stale."),
|
|
33
|
+
fields: z.record(z.unknown()).describe("The fields to change (camelCase)."),
|
|
34
|
+
}, async ({ personaId, expectedVersion, fields }) => guard(async () => jsonResult(await client.put(`/api/v1/personas/${personaId}`, {
|
|
35
|
+
expectedVersion,
|
|
36
|
+
...fields,
|
|
37
|
+
}))));
|
|
38
|
+
}
|
|
39
|
+
export function registerConnectorTools(server, client) {
|
|
40
|
+
const authGuard = (fields) => {
|
|
41
|
+
const authConfig = fields.authConfig;
|
|
42
|
+
if (authConfig && !Object.values(authConfig).every(isPlaceholder)) {
|
|
43
|
+
throw new Error("authConfig must contain only placeholders (empty or {{ … }}). Never put " +
|
|
44
|
+
"real credentials in a connector — a human fills secrets in the Axonity " +
|
|
45
|
+
"tool editor.");
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
server.tool("create_connector", "Create a connector draft (a tool of type 'connector'). Do NOT put real " +
|
|
49
|
+
"credentials in authConfig — use placeholders; a human fills secrets in Axonity.", {
|
|
50
|
+
fields: z
|
|
51
|
+
.record(z.unknown())
|
|
52
|
+
.describe("Connector fields (name, description, implementation, authConfig)."),
|
|
53
|
+
}, async ({ fields }) => guard(async () => {
|
|
54
|
+
authGuard(fields);
|
|
55
|
+
return jsonResult(await client.post("/api/v1/tools", { type: "connector", ...fields }));
|
|
56
|
+
}));
|
|
57
|
+
server.tool("update_connector", "Update a connector draft (a tool of type 'connector'). Read it first for its " +
|
|
58
|
+
"version; keep authConfig as placeholders only.", {
|
|
59
|
+
id: z.string().describe("The connector (tool) id."),
|
|
60
|
+
expectedVersion: z.number().int().describe("Version last read — 409 if stale."),
|
|
61
|
+
fields: z.record(z.unknown()).describe("Fields to change (camelCase)."),
|
|
62
|
+
}, async ({ id, expectedVersion, fields }) => guard(async () => {
|
|
63
|
+
authGuard(fields);
|
|
64
|
+
return jsonResult(await client.put(`/api/v1/tools/${id}`, {
|
|
65
|
+
expectedVersion,
|
|
66
|
+
type: "connector",
|
|
67
|
+
...fields,
|
|
68
|
+
}));
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
export function registerAttachTools(server, client) {
|
|
72
|
+
const attach = (name, description, path, firstArg, secondArg) => server.tool(name, description, {
|
|
73
|
+
[firstArg]: z.string(),
|
|
74
|
+
[secondArg]: z.string(),
|
|
75
|
+
}, async (args) => guard(async () => jsonResult(await client.post(path(args[firstArg], args[secondArg])))));
|
|
76
|
+
attach("attach_skill_to_agent", "Scope a skill to an agent so the agent uses it.", (agentId, skillId) => `/api/v1/agents/${agentId}/skills-v2/${skillId}`, "agentId", "skillId");
|
|
77
|
+
attach("attach_skill_to_workflow", "Scope a skill to a workflow.", (workflowId, skillId) => `/api/v1/workflows/${workflowId}/skills-v2/${skillId}`, "workflowId", "skillId");
|
|
78
|
+
attach("attach_policy_to_agent", "Scope a policy to an agent.", (agentId, policyId) => `/api/v1/agents/${agentId}/policies/${policyId}`, "agentId", "policyId");
|
|
79
|
+
attach("attach_reference_to_agent", "Scope a reference doc to an agent.", (agentId, refId) => `/api/v1/agents/${agentId}/reference-docs/${refId}`, "agentId", "refId");
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=extras.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extras.js","sourceRoot":"","sources":["../../src/tools/extras.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registers the read/draft/update MCP tools for one Axonity entity type.
|
|
3
|
+
*
|
|
4
|
+
* Every entity (workflow, agent, tool) gets the same verbs the internal
|
|
5
|
+
* Builder team has, minus direct publish:
|
|
6
|
+
* list_<entity> — GET /api/v1/<entity>s
|
|
7
|
+
* read_<entity> — GET /api/v1/<entity>s/{id}
|
|
8
|
+
* create_<entity> — POST /api/v1/<entity>s (a new draft)
|
|
9
|
+
* update_<entity> — PUT|PATCH /api/v1/<entity>s/{id} (read-then-write)
|
|
10
|
+
*
|
|
11
|
+
* The connector stays thin: entity fields pass through as JSON and the backend
|
|
12
|
+
* validates them (422 surfaces the field detail). Updates carry `expectedVersion`
|
|
13
|
+
* so a stale write 409s rather than clobbering — the agent re-reads and retries.
|
|
14
|
+
*
|
|
15
|
+
* `request_publish_*` never publishes — it creates a pending approval a human
|
|
16
|
+
* decides in Axonity (epic #652 C3).
|
|
17
|
+
*/
|
|
18
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
+
import type { AxonityClient } from "../client.js";
|
|
20
|
+
export interface EntityDef {
|
|
21
|
+
/** Singular tool noun, e.g. "workflow". */
|
|
22
|
+
singular: string;
|
|
23
|
+
/** REST collection path, e.g. "/api/v1/workflows". */
|
|
24
|
+
basePath: string;
|
|
25
|
+
/** HTTP verb the update route uses (workflows PATCH; agents/tools PUT). */
|
|
26
|
+
updateMethod: "PUT" | "PATCH";
|
|
27
|
+
/** One-line human description of the entity, for the tool docs. */
|
|
28
|
+
label: string;
|
|
29
|
+
}
|
|
30
|
+
export declare function registerEntityTools(server: McpServer, client: AxonityClient, def: EntityDef): void;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registers the read/draft/update MCP tools for one Axonity entity type.
|
|
3
|
+
*
|
|
4
|
+
* Every entity (workflow, agent, tool) gets the same verbs the internal
|
|
5
|
+
* Builder team has, minus direct publish:
|
|
6
|
+
* list_<entity> — GET /api/v1/<entity>s
|
|
7
|
+
* read_<entity> — GET /api/v1/<entity>s/{id}
|
|
8
|
+
* create_<entity> — POST /api/v1/<entity>s (a new draft)
|
|
9
|
+
* update_<entity> — PUT|PATCH /api/v1/<entity>s/{id} (read-then-write)
|
|
10
|
+
*
|
|
11
|
+
* The connector stays thin: entity fields pass through as JSON and the backend
|
|
12
|
+
* validates them (422 surfaces the field detail). Updates carry `expectedVersion`
|
|
13
|
+
* so a stale write 409s rather than clobbering — the agent re-reads and retries.
|
|
14
|
+
*
|
|
15
|
+
* `request_publish_*` never publishes — it creates a pending approval a human
|
|
16
|
+
* decides in Axonity (epic #652 C3).
|
|
17
|
+
*/
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { guard, jsonResult } from "./result.js";
|
|
20
|
+
export function registerEntityTools(server, client, def) {
|
|
21
|
+
const { singular, basePath, updateMethod, label } = def;
|
|
22
|
+
server.tool(`list_${singular}s`, `List all ${label} in your Axonity tenant (id, name, status, version).`, {}, async () => guard(async () => jsonResult(await client.get(basePath))));
|
|
23
|
+
server.tool(`read_${singular}`, `Read one ${singular} by id, including its current draft and version. ` +
|
|
24
|
+
`Read before you update — you need the current version.`, { id: z.string().describe(`The ${singular}'s id.`) }, async ({ id }) => guard(async () => jsonResult(await client.get(`${basePath}/${id}`))));
|
|
25
|
+
server.tool(`create_${singular}`, `Create a new ${singular} draft. Pass the entity's fields (camelCase) in ` +
|
|
26
|
+
`\`fields\`; the backend validates them. Returns the created ${singular} ` +
|
|
27
|
+
`with its id and version.`, {
|
|
28
|
+
fields: z
|
|
29
|
+
.record(z.unknown())
|
|
30
|
+
.describe(`The ${singular}'s fields as a JSON object (camelCase keys), e.g. ` +
|
|
31
|
+
`{ "name": "…", "description": "…" }.`),
|
|
32
|
+
}, async ({ fields }) => guard(async () => jsonResult(await client.post(basePath, fields))));
|
|
33
|
+
server.tool(`update_${singular}`, `Update a ${singular} draft. Read it first to get \`expectedVersion\`; on a ` +
|
|
34
|
+
`409 conflict, read again and retry. Pass only the fields you are ` +
|
|
35
|
+
`changing (camelCase) in \`fields\`.`, {
|
|
36
|
+
id: z.string().describe(`The ${singular}'s id.`),
|
|
37
|
+
expectedVersion: z
|
|
38
|
+
.number()
|
|
39
|
+
.int()
|
|
40
|
+
.describe("The version you last read — rejected with 409 if stale."),
|
|
41
|
+
fields: z
|
|
42
|
+
.record(z.unknown())
|
|
43
|
+
.describe(`The fields to change, as a JSON object (camelCase keys).`),
|
|
44
|
+
}, async ({ id, expectedVersion, fields }) => guard(async () => {
|
|
45
|
+
const body = { expectedVersion, ...fields };
|
|
46
|
+
const path = `${basePath}/${id}`;
|
|
47
|
+
const data = updateMethod === "PUT"
|
|
48
|
+
? await client.put(path, body)
|
|
49
|
+
: await client.patch(path, body);
|
|
50
|
+
return jsonResult(data);
|
|
51
|
+
}));
|
|
52
|
+
server.tool(`request_publish_${singular}`, `Request that a ${singular} be published. This does NOT publish it — it ` +
|
|
53
|
+
`creates a pending approval a human approves in Axonity. Returns the ` +
|
|
54
|
+
`pending approval (with its readiness).`, {
|
|
55
|
+
id: z.string().describe(`The ${singular}'s id.`),
|
|
56
|
+
changeSummary: z
|
|
57
|
+
.string()
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("A short note for the approver on what changed and why."),
|
|
60
|
+
}, async ({ id, changeSummary }) => guard(async () => jsonResult(await client.post("/api/v1/publish-approvals", {
|
|
61
|
+
entityType: singular,
|
|
62
|
+
entityId: id,
|
|
63
|
+
...(changeSummary ? { changeSummary } : {}),
|
|
64
|
+
}))));
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/tools/register.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared MCP tool-result helpers: turn API data into a text result, map errors
|
|
3
|
+
* to a clean error result, and guard a handler so nothing throws out of a tool.
|
|
4
|
+
*/
|
|
5
|
+
/** A tool result carrying a JSON payload as pretty text. */
|
|
6
|
+
export declare function jsonResult(data: unknown): {
|
|
7
|
+
content: {
|
|
8
|
+
type: "text";
|
|
9
|
+
text: string;
|
|
10
|
+
}[];
|
|
11
|
+
};
|
|
12
|
+
/** A tool result flagged as an error, with a clean message. */
|
|
13
|
+
export declare function errorResult(err: unknown): {
|
|
14
|
+
content: {
|
|
15
|
+
type: "text";
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
18
|
+
isError: boolean;
|
|
19
|
+
};
|
|
20
|
+
/** Wrap a handler so any thrown error becomes a clean tool error. */
|
|
21
|
+
export declare function guard(fn: () => Promise<ReturnType<typeof jsonResult>>): Promise<ReturnType<typeof jsonResult> | ReturnType<typeof errorResult>>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared MCP tool-result helpers: turn API data into a text result, map errors
|
|
3
|
+
* to a clean error result, and guard a handler so nothing throws out of a tool.
|
|
4
|
+
*/
|
|
5
|
+
import { AxonityApiError } from "../errors.js";
|
|
6
|
+
/** A tool result carrying a JSON payload as pretty text. */
|
|
7
|
+
export function jsonResult(data) {
|
|
8
|
+
return {
|
|
9
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** A tool result flagged as an error, with a clean message. */
|
|
13
|
+
export function errorResult(err) {
|
|
14
|
+
const message = err instanceof AxonityApiError
|
|
15
|
+
? err.message
|
|
16
|
+
: err instanceof Error
|
|
17
|
+
? err.message
|
|
18
|
+
: String(err);
|
|
19
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
20
|
+
}
|
|
21
|
+
/** Wrap a handler so any thrown error becomes a clean tool error. */
|
|
22
|
+
export async function guard(fn) {
|
|
23
|
+
try {
|
|
24
|
+
return await fn();
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return errorResult(err);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/tools/result.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,4DAA4D;AAC5D,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;KAC1E,CAAC;AACJ,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,MAAM,OAAO,GACX,GAAG,YAAY,eAAe;QAC5B,CAAC,CAAC,GAAG,CAAC,OAAO;QACb,CAAC,CAAC,GAAG,YAAY,KAAK;YACpB,CAAC,CAAC,GAAG,CAAC,OAAO;YACb,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACpB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAChF,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,EAAgD;IAEhD,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `apply_workflow_mutations` — structural edits to a workflow document.
|
|
3
|
+
*
|
|
4
|
+
* A workflow's steps and edges are edited through mutation COMMANDS (the same
|
|
5
|
+
* backend-authoritative model the Studio uses), never by replacing the whole
|
|
6
|
+
* document. The backend applies and validates the commands and returns the new
|
|
7
|
+
* authoritative document + version.
|
|
8
|
+
*/
|
|
9
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import type { AxonityClient } from "../client.js";
|
|
11
|
+
export declare function registerWorkflowMutations(server: McpServer, client: AxonityClient): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `apply_workflow_mutations` — structural edits to a workflow document.
|
|
3
|
+
*
|
|
4
|
+
* A workflow's steps and edges are edited through mutation COMMANDS (the same
|
|
5
|
+
* backend-authoritative model the Studio uses), never by replacing the whole
|
|
6
|
+
* document. The backend applies and validates the commands and returns the new
|
|
7
|
+
* authoritative document + version.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { AxonityApiError } from "../errors.js";
|
|
11
|
+
export function registerWorkflowMutations(server, client) {
|
|
12
|
+
server.tool("apply_workflow_mutations", "Apply structural mutation commands to a workflow draft (add/remove steps, " +
|
|
13
|
+
"connect edges, set conditions). Read the workflow first for its version. " +
|
|
14
|
+
"Returns the new authoritative document + version. On a 409 conflict, " +
|
|
15
|
+
"re-read and retry.", {
|
|
16
|
+
id: z.string().describe("The workflow's id."),
|
|
17
|
+
expectedVersion: z
|
|
18
|
+
.number()
|
|
19
|
+
.int()
|
|
20
|
+
.describe("The version you last read — rejected with 409 if stale."),
|
|
21
|
+
mutations: z
|
|
22
|
+
.array(z.record(z.unknown()))
|
|
23
|
+
.describe("A list of mutation commands, each a JSON object with a command name " +
|
|
24
|
+
"and its arguments (camelCase)."),
|
|
25
|
+
}, async ({ id, expectedVersion, mutations }) => {
|
|
26
|
+
try {
|
|
27
|
+
const data = await client.post(`/api/v1/workflows/${id}/mutations`, {
|
|
28
|
+
expectedVersion,
|
|
29
|
+
mutations,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
const message = err instanceof AxonityApiError
|
|
37
|
+
? err.message
|
|
38
|
+
: err instanceof Error
|
|
39
|
+
? err.message
|
|
40
|
+
: String(err);
|
|
41
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=workflowMutations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflowMutations.js","sourceRoot":"","sources":["../../src/tools/workflowMutations.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,UAAU,yBAAyB,CACvC,MAAiB,EACjB,MAAqB;IAErB,MAAM,CAAC,IAAI,CACT,0BAA0B,EAC1B,4EAA4E;QAC1E,2EAA2E;QAC3E,uEAAuE;QACvE,oBAAoB,EACtB;QACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;QAC7C,eAAe,EAAE,CAAC;aACf,MAAM,EAAE;aACR,GAAG,EAAE;aACL,QAAQ,CAAC,yDAAyD,CAAC;QACtE,SAAS,EAAE,CAAC;aACT,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;aAC5B,QAAQ,CACP,sEAAsE;YACpE,gCAAgC,CACnC;KACJ,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE,SAAS,EAAE,EAAE,EAAE;QAC3C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,YAAY,EAAE;gBAClE,eAAe;gBACf,SAAS;aACV,CAAC,CAAC;YACH,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aAC1E,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GACX,GAAG,YAAY,eAAe;gBAC5B,CAAC,CAAC,GAAG,CAAC,OAAO;gBACb,CAAC,CAAC,GAAG,YAAY,KAAK;oBACpB,CAAC,CAAC,GAAG,CAAC,OAAO;oBACb,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACpB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAChF,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@axonity-ai/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Model Context Protocol server for Axonity Flow — lets an external agent (e.g. Claude Code) read, draft, and update workflows, agents and tools in an Axonity tenant.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/AxonityAI/axonity-mcp.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/AxonityAI/axonity-mcp#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/AxonityAI/axonity-mcp/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"model-context-protocol",
|
|
17
|
+
"axonity",
|
|
18
|
+
"claude",
|
|
19
|
+
"agent"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"bin": {
|
|
23
|
+
"axonity-mcp": "dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc -p tsconfig.json",
|
|
36
|
+
"dev": "tsx src/index.ts",
|
|
37
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest",
|
|
40
|
+
"prepublishOnly": "npm run build"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
44
|
+
"zod": "^3.23.8"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^20.14.0",
|
|
48
|
+
"tsx": "^4.16.0",
|
|
49
|
+
"typescript": "^5.5.0",
|
|
50
|
+
"vitest": "^2.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|