@marchward/mcp-server 0.2.2
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 +81 -0
- package/dist/api-client.d.ts +68 -0
- package/dist/api-client.js +144 -0
- package/dist/api-client.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +197 -0
- package/dist/index.js.map +1 -0
- package/dist/tools.d.ts +211 -0
- package/dist/tools.js +238 -0
- package/dist/tools.js.map +1 -0
- package/glama.json +4 -0
- package/package.json +35 -0
- package/src/.fuse_hidden0000004000000001 +286 -0
- package/src/api-client.ts +224 -0
- package/src/index.ts +245 -0
- package/src/tools.ts +286 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Marchward MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes Marchward governance operations as MCP tools for AI coding agents.
|
|
6
|
+
* Supports two transports:
|
|
7
|
+
* - stdio: Single-user, local. API key from MARCHWARD_API_KEY env var.
|
|
8
|
+
* - http: Multi-tenant shared service. Each user passes their own
|
|
9
|
+
* API key via the Authorization header in their MCP config.
|
|
10
|
+
* The server holds NO credentials — it's a stateless proxy.
|
|
11
|
+
*
|
|
12
|
+
* Environment variables (TENET_* names still read as a fallback for back-compat):
|
|
13
|
+
* MARCHWARD_API_URL — Marchward API base URL (required)
|
|
14
|
+
* MARCHWARD_API_KEY — Marchward API key (required for stdio only)
|
|
15
|
+
* PORT — HTTP server port (default: 3100, http transport only)
|
|
16
|
+
* TRANSPORT — "stdio" or "http" (default: "stdio")
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
20
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
21
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
22
|
+
import express from "express";
|
|
23
|
+
import { randomUUID } from "node:crypto";
|
|
24
|
+
|
|
25
|
+
import { MarchwardAPIClient } from "./api-client.js";
|
|
26
|
+
import { TOOL_DEFINITIONS, createToolHandlers } from "./tools.js";
|
|
27
|
+
|
|
28
|
+
// ─── Configuration ──────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
const MARCHWARD_API_URL = (process.env.MARCHWARD_API_URL ?? process.env.TENET_API_URL);
|
|
31
|
+
const MARCHWARD_API_KEY = (process.env.MARCHWARD_API_KEY ?? process.env.TENET_API_KEY);
|
|
32
|
+
const PORT = parseInt(process.env.PORT ?? "3100", 10);
|
|
33
|
+
const TRANSPORT = process.env.TRANSPORT ?? "stdio";
|
|
34
|
+
|
|
35
|
+
// ─── Build Server ───────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create an MCP server wired to a specific Marchward API client.
|
|
39
|
+
* For stdio: one client per process (key from env).
|
|
40
|
+
* For HTTP: one client per session (key from request headers).
|
|
41
|
+
*/
|
|
42
|
+
function createMcpServer(apiKey: string): McpServer {
|
|
43
|
+
if (!MARCHWARD_API_URL) {
|
|
44
|
+
throw new Error("MARCHWARD_API_URL environment variable is required");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const server = new McpServer({
|
|
48
|
+
name: "marchward",
|
|
49
|
+
version: "0.2.1",
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const client = new MarchwardAPIClient({
|
|
53
|
+
apiUrl: MARCHWARD_API_URL,
|
|
54
|
+
apiKey,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const handlers = createToolHandlers(client);
|
|
58
|
+
|
|
59
|
+
// Register each tool with the MCP server
|
|
60
|
+
for (const [key, def] of Object.entries(TOOL_DEFINITIONS)) {
|
|
61
|
+
const handlerFn = handlers[key as keyof typeof handlers];
|
|
62
|
+
if (handlerFn) {
|
|
63
|
+
server.tool(
|
|
64
|
+
def.name,
|
|
65
|
+
def.description,
|
|
66
|
+
def.inputSchema.shape,
|
|
67
|
+
handlerFn as any,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return server;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── stdio Transport (single-user, local) ───────────────────────
|
|
76
|
+
|
|
77
|
+
async function runStdio() {
|
|
78
|
+
if (!MARCHWARD_API_KEY) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
"MARCHWARD_API_KEY environment variable is required for stdio transport",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const server = createMcpServer(MARCHWARD_API_KEY);
|
|
85
|
+
const transport = new StdioServerTransport();
|
|
86
|
+
await server.connect(transport);
|
|
87
|
+
console.error("[marchward-mcp] Running on stdio transport");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ─── HTTP Transport (multi-tenant, shared service) ──────────────
|
|
91
|
+
|
|
92
|
+
function extractApiKey(req: express.Request): string | null {
|
|
93
|
+
// 1. Check Authorization header (standard MCP auth)
|
|
94
|
+
const auth = req.headers["authorization"];
|
|
95
|
+
if (auth) {
|
|
96
|
+
if (auth.startsWith("Bearer ")) {
|
|
97
|
+
return auth.slice(7);
|
|
98
|
+
}
|
|
99
|
+
if (auth.startsWith("mw_") || auth.startsWith("tnt_")) {
|
|
100
|
+
return auth;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 2. Check URL query parameter (for clients like Claude's custom connector
|
|
105
|
+
// that don't support static Authorization headers — they use OAuth or authless)
|
|
106
|
+
const queryKey = req.query.key as string | undefined;
|
|
107
|
+
if (queryKey?.startsWith("mw_") || queryKey?.startsWith("tnt_")) {
|
|
108
|
+
return queryKey;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 3. Fall back to environment variable (single-tenant deployment mode)
|
|
112
|
+
if (MARCHWARD_API_KEY) {
|
|
113
|
+
return MARCHWARD_API_KEY;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface SessionEntry {
|
|
120
|
+
transport: StreamableHTTPServerTransport;
|
|
121
|
+
apiKey: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function runHttp() {
|
|
125
|
+
if (!MARCHWARD_API_URL) {
|
|
126
|
+
throw new Error("MARCHWARD_API_URL environment variable is required");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const app = express();
|
|
130
|
+
app.use(express.json());
|
|
131
|
+
|
|
132
|
+
// Request logging for debugging connector issues
|
|
133
|
+
app.use((req, _res, next) => {
|
|
134
|
+
const hasAuth = !!req.headers["authorization"];
|
|
135
|
+
const hasQueryKey = !!req.query.key;
|
|
136
|
+
const hasEnvKey = !!MARCHWARD_API_KEY;
|
|
137
|
+
console.log(
|
|
138
|
+
`[marchward-mcp] ${req.method} ${req.path} | auth-header: ${hasAuth} | query-key: ${hasQueryKey} | env-fallback: ${hasEnvKey}`,
|
|
139
|
+
);
|
|
140
|
+
next();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Track active sessions (transport + the API key they authenticated with)
|
|
144
|
+
const sessions = new Map<string, SessionEntry>();
|
|
145
|
+
|
|
146
|
+
app.post("/mcp", async (req, res) => {
|
|
147
|
+
const sessionId = req.headers["mcp-session-id"] as string | undefined;
|
|
148
|
+
|
|
149
|
+
// ── Existing session ──────────────────────────────────────
|
|
150
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
151
|
+
const entry = sessions.get(sessionId)!;
|
|
152
|
+
await entry.transport.handleRequest(req, res, req.body);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── New session — extract user's API key from headers ─────
|
|
157
|
+
const apiKey = extractApiKey(req);
|
|
158
|
+
if (!apiKey) {
|
|
159
|
+
res.status(401).json({
|
|
160
|
+
error: "Missing Authorization header. Include your Marchward API key as: Authorization: Bearer mw_your_key",
|
|
161
|
+
});
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Create a server + client scoped to this user's API key
|
|
166
|
+
const server = createMcpServer(apiKey);
|
|
167
|
+
const transport = new StreamableHTTPServerTransport({
|
|
168
|
+
sessionIdGenerator: () => randomUUID(),
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
transport.onclose = () => {
|
|
172
|
+
const sid = transport.sessionId;
|
|
173
|
+
if (sid) sessions.delete(sid);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
await server.connect(transport);
|
|
177
|
+
|
|
178
|
+
// Handle the initial request
|
|
179
|
+
await transport.handleRequest(req, res, req.body);
|
|
180
|
+
|
|
181
|
+
// Store session for subsequent requests
|
|
182
|
+
if (transport.sessionId) {
|
|
183
|
+
sessions.set(transport.sessionId, { transport, apiKey });
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Handle GET for SSE streams
|
|
188
|
+
app.get("/mcp", async (req, res) => {
|
|
189
|
+
const sessionId = req.headers["mcp-session-id"] as string | undefined;
|
|
190
|
+
if (!sessionId || !sessions.has(sessionId)) {
|
|
191
|
+
res.status(400).json({ error: "Invalid or missing session ID" });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const entry = sessions.get(sessionId)!;
|
|
195
|
+
await entry.transport.handleRequest(req, res);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Handle DELETE for session cleanup
|
|
199
|
+
app.delete("/mcp", async (req, res) => {
|
|
200
|
+
const sessionId = req.headers["mcp-session-id"] as string | undefined;
|
|
201
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
202
|
+
const entry = sessions.get(sessionId)!;
|
|
203
|
+
await entry.transport.close();
|
|
204
|
+
sessions.delete(sessionId);
|
|
205
|
+
}
|
|
206
|
+
res.status(200).json({ ok: true });
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Health check (no auth required)
|
|
210
|
+
app.get("/health", (_req, res) => {
|
|
211
|
+
res.json({
|
|
212
|
+
status: "ok",
|
|
213
|
+
server: "marchward-mcp-server",
|
|
214
|
+
version: "0.2.1",
|
|
215
|
+
transport: "streamable-http",
|
|
216
|
+
activeSessions: sessions.size,
|
|
217
|
+
marchwardApiUrl: MARCHWARD_API_URL,
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
app.listen(PORT, () => {
|
|
222
|
+
console.log(`[marchward-mcp] HTTP server listening on port ${PORT}`);
|
|
223
|
+
console.log(`[marchward-mcp] MCP endpoint: http://localhost:${PORT}/mcp`);
|
|
224
|
+
console.log(`[marchward-mcp] Health check: http://localhost:${PORT}/health`);
|
|
225
|
+
console.log(`[marchward-mcp] Marchward API: ${MARCHWARD_API_URL}`);
|
|
226
|
+
console.log(`[marchward-mcp] Mode: multi-tenant (API key per session from Authorization header)`);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ─── Entry Point ────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
async function main() {
|
|
233
|
+
try {
|
|
234
|
+
if (TRANSPORT === "http") {
|
|
235
|
+
await runHttp();
|
|
236
|
+
} else {
|
|
237
|
+
await runStdio();
|
|
238
|
+
}
|
|
239
|
+
} catch (err) {
|
|
240
|
+
console.error("[marchward-mcp] Fatal error:", err);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
main();
|
package/src/tools.ts
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marchward MCP Server — Tool definitions
|
|
3
|
+
*
|
|
4
|
+
* Each tool maps to a Marchward API capability. These tools let
|
|
5
|
+
* AI coding agents (Claude, Cursor, Copilot, etc.):
|
|
6
|
+
* 1. Check governance before taking actions (marchward_authorize)
|
|
7
|
+
* 2. Execute actions through Marchward's credential gateway (marchward_execute)
|
|
8
|
+
* 3. Register and manage agents (marchward_register_agent, marchward_list_agents)
|
|
9
|
+
* 4. Inspect decisions (marchward_get_decisions)
|
|
10
|
+
* 5. Check governance coverage (marchward_check_coverage)
|
|
11
|
+
* 6. Bind policies to agents (marchward_bind_policy)
|
|
12
|
+
*
|
|
13
|
+
* Tool names are marchward_* (stable as of v0.2.0). MCP clients discover
|
|
14
|
+
* tools dynamically at runtime; the names are not dual-listed because
|
|
15
|
+
* duplicate tools degrade agent tool-selection.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { MarchwardAPIClient } from "./api-client.js";
|
|
20
|
+
|
|
21
|
+
// ─── Tool Schemas ───────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
export const TOOL_DEFINITIONS = {
|
|
24
|
+
marchward_authorize: {
|
|
25
|
+
name: "marchward_authorize",
|
|
26
|
+
description:
|
|
27
|
+
"Check whether an action is allowed BEFORE the agent takes it. Call this first for any consequential or irreversible action (deploying, committing, publishing, deleting, moving money, calling an external API). Returns ALLOW, BLOCK, ESCALATE, or ALLOW_WITH_CONDITIONS, and writes a tamper-evident audit record. The agent and policy are resolved from your API key, so you usually only pass toolName.",
|
|
28
|
+
inputSchema: z.object({
|
|
29
|
+
toolName: z
|
|
30
|
+
.string()
|
|
31
|
+
.describe(
|
|
32
|
+
"Name of the tool/action being authorized (e.g. 'github_commit', 'railway_deploy', 'notion_update')",
|
|
33
|
+
),
|
|
34
|
+
arguments: z
|
|
35
|
+
.record(z.unknown())
|
|
36
|
+
.optional()
|
|
37
|
+
.describe("Arguments/parameters for the tool call"),
|
|
38
|
+
policyBundleId: z
|
|
39
|
+
.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("ID of the policy bundle to evaluate against (auto-resolved from API key → agent → policy if omitted)"),
|
|
42
|
+
agentId: z
|
|
43
|
+
.string()
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("Agent ID (auto-resolved from API key if omitted)"),
|
|
46
|
+
mode: z
|
|
47
|
+
.enum(["HIC", "HITL", "HOTL"])
|
|
48
|
+
.optional()
|
|
49
|
+
.describe(
|
|
50
|
+
"Governance mode: HIC (human-in-command), HITL (human-in-the-loop), HOTL (human-on-the-loop)",
|
|
51
|
+
),
|
|
52
|
+
context: z
|
|
53
|
+
.record(z.unknown())
|
|
54
|
+
.optional()
|
|
55
|
+
.describe("Additional context for policy evaluation"),
|
|
56
|
+
}),
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
marchward_execute: {
|
|
60
|
+
name: "marchward_execute",
|
|
61
|
+
description:
|
|
62
|
+
"Run an action through Marchward's credential-mediated gateway. Marchward evaluates policy, injects the real downstream credential server-side (the agent never holds it), enforces the cost cap and approval gates, then makes the call and records it. Use this for any call to a service whose credentials Marchward manages (GitHub, Stripe, Notion, etc.).",
|
|
63
|
+
inputSchema: z.object({
|
|
64
|
+
toolName: z
|
|
65
|
+
.string()
|
|
66
|
+
.describe("Name of the tool/action being executed"),
|
|
67
|
+
arguments: z
|
|
68
|
+
.record(z.unknown())
|
|
69
|
+
.optional()
|
|
70
|
+
.describe("Arguments for the tool call"),
|
|
71
|
+
policyBundleId: z
|
|
72
|
+
.string()
|
|
73
|
+
.describe("Policy bundle ID to evaluate against"),
|
|
74
|
+
agentId: z.string().describe("Agent ID making the request"),
|
|
75
|
+
service: z
|
|
76
|
+
.string()
|
|
77
|
+
.describe(
|
|
78
|
+
"Service name for credential injection (e.g. 'github', 'railway', 'notion')",
|
|
79
|
+
),
|
|
80
|
+
downstream: z.object({
|
|
81
|
+
url: z.string().describe("Full URL for the downstream API call"),
|
|
82
|
+
method: z
|
|
83
|
+
.enum(["GET", "POST", "PUT", "PATCH", "DELETE"])
|
|
84
|
+
.describe("HTTP method"),
|
|
85
|
+
headers: z
|
|
86
|
+
.record(z.string())
|
|
87
|
+
.optional()
|
|
88
|
+
.describe("Additional headers"),
|
|
89
|
+
body: z.unknown().optional().describe("Request body"),
|
|
90
|
+
}),
|
|
91
|
+
mode: z.enum(["HIC", "HITL", "HOTL"]).optional(),
|
|
92
|
+
}),
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
marchward_register_agent: {
|
|
96
|
+
name: "marchward_register_agent",
|
|
97
|
+
description:
|
|
98
|
+
"Register a new agent with Marchward so it is governed from its first action. Returns the agent record and its Marchward API key — the only credential the agent should hold. Call this when building or deploying a new agent.",
|
|
99
|
+
inputSchema: z.object({
|
|
100
|
+
name: z.string().describe("Human-readable name for the agent"),
|
|
101
|
+
description: z
|
|
102
|
+
.string()
|
|
103
|
+
.optional()
|
|
104
|
+
.describe("Description of what the agent does"),
|
|
105
|
+
agentId: z
|
|
106
|
+
.string()
|
|
107
|
+
.optional()
|
|
108
|
+
.describe(
|
|
109
|
+
"Custom agent ID (auto-generated from name if omitted)",
|
|
110
|
+
),
|
|
111
|
+
}),
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
marchward_list_agents: {
|
|
115
|
+
name: "marchward_list_agents",
|
|
116
|
+
description:
|
|
117
|
+
"List the agents registered under your account, with their IDs, status, last-seen time, and bound policy. Use to find an agentId or check what is under governance.",
|
|
118
|
+
inputSchema: z.object({}),
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
marchward_get_decisions: {
|
|
122
|
+
name: "marchward_get_decisions",
|
|
123
|
+
description:
|
|
124
|
+
"Review recent governance decisions (the audit trail). Filter by agent, tool name, or outcome (ALLOW/BLOCK/ESCALATE). Use to verify governance is active or to investigate what an agent actually did.",
|
|
125
|
+
inputSchema: z.object({
|
|
126
|
+
agentId: z
|
|
127
|
+
.string()
|
|
128
|
+
.optional()
|
|
129
|
+
.describe("Filter decisions by agent ID"),
|
|
130
|
+
toolName: z
|
|
131
|
+
.string()
|
|
132
|
+
.optional()
|
|
133
|
+
.describe("Filter decisions by tool name"),
|
|
134
|
+
result: z
|
|
135
|
+
.enum(["ALLOW", "BLOCK", "ESCALATE", "ALLOW_WITH_CONDITIONS"])
|
|
136
|
+
.optional()
|
|
137
|
+
.describe("Filter by decision outcome"),
|
|
138
|
+
limit: z
|
|
139
|
+
.number()
|
|
140
|
+
.optional()
|
|
141
|
+
.default(20)
|
|
142
|
+
.describe("Number of decisions to return (default 20)"),
|
|
143
|
+
}),
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
marchward_check_coverage: {
|
|
147
|
+
name: "marchward_check_coverage",
|
|
148
|
+
description:
|
|
149
|
+
"Find governance gaps for an agent by comparing its declared tools against the tools it has actually called. Returns a coverage percentage and the uncovered tools, so ungoverned actions surface before they cause harm. This is the 'Dependabot for governance'.",
|
|
150
|
+
inputSchema: z.object({
|
|
151
|
+
agentId: z.string().describe("Agent ID to check coverage for"),
|
|
152
|
+
}),
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
marchward_bind_policy: {
|
|
156
|
+
name: "marchward_bind_policy",
|
|
157
|
+
description:
|
|
158
|
+
"Attach a governance policy to an agent. After binding, every authorize and execute call from that agent is evaluated against this policy.",
|
|
159
|
+
inputSchema: z.object({
|
|
160
|
+
agentId: z.string().describe("Agent ID to bind the policy to"),
|
|
161
|
+
policyBundleId: z.string().describe("Policy bundle ID to bind"),
|
|
162
|
+
}),
|
|
163
|
+
},
|
|
164
|
+
} as const;
|
|
165
|
+
|
|
166
|
+
// ─── Tool Handlers ──────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
export function createToolHandlers(client: MarchwardAPIClient) {
|
|
169
|
+
return {
|
|
170
|
+
async marchward_authorize(args: z.infer<typeof TOOL_DEFINITIONS.marchward_authorize.inputSchema>) {
|
|
171
|
+
const result = await client.authorize(args);
|
|
172
|
+
const decision = (result as any).result ?? (result as any).decision ?? "UNKNOWN";
|
|
173
|
+
const decisionId = (result as any).decisionId ?? "";
|
|
174
|
+
const explanation = (result as any).explanation ?? [];
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
content: [
|
|
178
|
+
{
|
|
179
|
+
type: "text" as const,
|
|
180
|
+
text: JSON.stringify(
|
|
181
|
+
{
|
|
182
|
+
decision,
|
|
183
|
+
decisionId,
|
|
184
|
+
explanation,
|
|
185
|
+
toolName: args.toolName,
|
|
186
|
+
...(args.policyBundleId ? { policyBundleId: args.policyBundleId } : {}),
|
|
187
|
+
full: result,
|
|
188
|
+
},
|
|
189
|
+
null,
|
|
190
|
+
2,
|
|
191
|
+
),
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
async marchward_execute(args: z.infer<typeof TOOL_DEFINITIONS.marchward_execute.inputSchema>) {
|
|
198
|
+
const result = await client.execute(args);
|
|
199
|
+
return {
|
|
200
|
+
content: [
|
|
201
|
+
{
|
|
202
|
+
type: "text" as const,
|
|
203
|
+
text: JSON.stringify(result, null, 2),
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
async marchward_register_agent(args: z.infer<typeof TOOL_DEFINITIONS.marchward_register_agent.inputSchema>) {
|
|
210
|
+
const result = await client.createAgent(args);
|
|
211
|
+
return {
|
|
212
|
+
content: [
|
|
213
|
+
{
|
|
214
|
+
type: "text" as const,
|
|
215
|
+
text: JSON.stringify(
|
|
216
|
+
{
|
|
217
|
+
message: `Agent "${args.name}" registered successfully.`,
|
|
218
|
+
...result,
|
|
219
|
+
},
|
|
220
|
+
null,
|
|
221
|
+
2,
|
|
222
|
+
),
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
async marchward_list_agents(_args: z.infer<typeof TOOL_DEFINITIONS.marchward_list_agents.inputSchema>) {
|
|
229
|
+
const result = await client.listAgents();
|
|
230
|
+
return {
|
|
231
|
+
content: [
|
|
232
|
+
{
|
|
233
|
+
type: "text" as const,
|
|
234
|
+
text: JSON.stringify(result, null, 2),
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
async marchward_get_decisions(args: z.infer<typeof TOOL_DEFINITIONS.marchward_get_decisions.inputSchema>) {
|
|
241
|
+
const result = await client.listDecisions(args);
|
|
242
|
+
return {
|
|
243
|
+
content: [
|
|
244
|
+
{
|
|
245
|
+
type: "text" as const,
|
|
246
|
+
text: JSON.stringify(result, null, 2),
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
};
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
async marchward_check_coverage(args: z.infer<typeof TOOL_DEFINITIONS.marchward_check_coverage.inputSchema>) {
|
|
253
|
+
const result = await client.checkCoverage(args.agentId);
|
|
254
|
+
return {
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
type: "text" as const,
|
|
258
|
+
text: JSON.stringify(result, null, 2),
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
async marchward_bind_policy(args: z.infer<typeof TOOL_DEFINITIONS.marchward_bind_policy.inputSchema>) {
|
|
265
|
+
const result = await client.bindPolicy(
|
|
266
|
+
args.agentId,
|
|
267
|
+
args.policyBundleId,
|
|
268
|
+
);
|
|
269
|
+
return {
|
|
270
|
+
content: [
|
|
271
|
+
{
|
|
272
|
+
type: "text" as const,
|
|
273
|
+
text: JSON.stringify(
|
|
274
|
+
{
|
|
275
|
+
message: `Policy "${args.policyBundleId}" bound to agent "${args.agentId}".`,
|
|
276
|
+
...result,
|
|
277
|
+
},
|
|
278
|
+
null,
|
|
279
|
+
2,
|
|
280
|
+
),
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|