ont-run 0.0.4 → 0.0.6
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 +8 -4
- package/dist/bin/ont.js +14000 -5172
- package/dist/index.js +15820 -7206
- package/dist/src/browser/server.d.ts +2 -0
- package/dist/src/browser/transform.d.ts +0 -1
- package/dist/src/config/define.d.ts +38 -3
- package/dist/src/config/schema.d.ts +20 -116
- package/dist/src/config/types.d.ts +27 -27
- package/dist/src/config/zod-utils.d.ts +44 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/server/api/index.d.ts +0 -2
- package/dist/src/server/api/router.d.ts +1 -1
- package/dist/src/server/mcp/index.d.ts +0 -2
- package/dist/src/server/mcp/tools.d.ts +2 -2
- package/dist/src/server/resolver.d.ts +4 -15
- package/package.json +2 -3
- package/src/browser/server.ts +203 -2
- package/src/browser/transform.ts +40 -19
- package/src/cli/commands/init.ts +10 -5
- package/src/cli/commands/review.ts +2 -1
- package/src/config/define.ts +52 -2
- package/src/config/schema.ts +49 -34
- package/src/config/types.ts +33 -31
- package/src/config/zod-utils.ts +144 -0
- package/src/index.ts +3 -2
- package/src/lockfile/hasher.ts +46 -22
- package/src/server/api/index.ts +2 -15
- package/src/server/api/router.ts +3 -6
- package/src/server/mcp/index.ts +2 -4
- package/src/server/mcp/tools.ts +48 -27
- package/src/server/resolver.ts +5 -78
- package/src/server/start.ts +0 -2
package/src/config/types.ts
CHANGED
|
@@ -37,12 +37,41 @@ export interface FieldOption {
|
|
|
37
37
|
label: string;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Context passed to resolvers
|
|
42
|
+
*/
|
|
43
|
+
export interface ResolverContext {
|
|
44
|
+
/** Current environment name */
|
|
45
|
+
env: string;
|
|
46
|
+
/** Environment configuration */
|
|
47
|
+
envConfig: EnvironmentConfig;
|
|
48
|
+
/** Logger instance */
|
|
49
|
+
logger: {
|
|
50
|
+
info: (message: string, ...args: unknown[]) => void;
|
|
51
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
52
|
+
error: (message: string, ...args: unknown[]) => void;
|
|
53
|
+
debug: (message: string, ...args: unknown[]) => void;
|
|
54
|
+
};
|
|
55
|
+
/** Access groups for the current request */
|
|
56
|
+
accessGroups: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolver function signature
|
|
61
|
+
*/
|
|
62
|
+
export type ResolverFunction<TArgs = unknown, TResult = unknown> = (
|
|
63
|
+
ctx: ResolverContext,
|
|
64
|
+
args: TArgs
|
|
65
|
+
) => Promise<TResult> | TResult;
|
|
66
|
+
|
|
40
67
|
/**
|
|
41
68
|
* Definition of a function in the ontology
|
|
42
69
|
*/
|
|
43
70
|
export interface FunctionDefinition<
|
|
44
71
|
TGroups extends string = string,
|
|
45
72
|
TEntities extends string = string,
|
|
73
|
+
TInputs extends z.ZodType = z.ZodType<unknown>,
|
|
74
|
+
TOutputs extends z.ZodType = z.ZodType<unknown>,
|
|
46
75
|
> {
|
|
47
76
|
/** Human-readable description of what this function does */
|
|
48
77
|
description: string;
|
|
@@ -51,11 +80,11 @@ export interface FunctionDefinition<
|
|
|
51
80
|
/** Which entities this function relates to (use empty array [] if none) */
|
|
52
81
|
entities: TEntities[];
|
|
53
82
|
/** Zod schema for input validation */
|
|
54
|
-
inputs:
|
|
83
|
+
inputs: TInputs;
|
|
55
84
|
/** Zod schema for output validation/documentation */
|
|
56
|
-
outputs?:
|
|
57
|
-
/**
|
|
58
|
-
resolver:
|
|
85
|
+
outputs?: TOutputs;
|
|
86
|
+
/** Resolver function that handles this function's logic */
|
|
87
|
+
resolver: ResolverFunction<z.infer<TInputs>, z.infer<TOutputs>>;
|
|
59
88
|
}
|
|
60
89
|
|
|
61
90
|
/**
|
|
@@ -105,30 +134,3 @@ export interface OntologyConfig<
|
|
|
105
134
|
/** Function definitions */
|
|
106
135
|
functions: TFunctions;
|
|
107
136
|
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Context passed to resolvers
|
|
111
|
-
*/
|
|
112
|
-
export interface ResolverContext {
|
|
113
|
-
/** Current environment name */
|
|
114
|
-
env: string;
|
|
115
|
-
/** Environment configuration */
|
|
116
|
-
envConfig: EnvironmentConfig;
|
|
117
|
-
/** Logger instance */
|
|
118
|
-
logger: {
|
|
119
|
-
info: (message: string, ...args: unknown[]) => void;
|
|
120
|
-
warn: (message: string, ...args: unknown[]) => void;
|
|
121
|
-
error: (message: string, ...args: unknown[]) => void;
|
|
122
|
-
debug: (message: string, ...args: unknown[]) => void;
|
|
123
|
-
};
|
|
124
|
-
/** Access groups for the current request */
|
|
125
|
-
accessGroups: string[];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Resolver function signature
|
|
130
|
-
*/
|
|
131
|
-
export type ResolverFunction<TArgs = unknown, TResult = unknown> = (
|
|
132
|
-
ctx: ResolverContext,
|
|
133
|
-
args: TArgs
|
|
134
|
-
) => Promise<TResult> | TResult;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for inspecting Zod schemas.
|
|
3
|
+
* These use duck typing to work across Zod 3 and Zod 4.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if a value is a Zod schema (duck typing to work across bundle boundaries and Zod versions)
|
|
8
|
+
*/
|
|
9
|
+
export function isZodSchema(val: unknown): boolean {
|
|
10
|
+
if (val === null || typeof val !== "object") return false;
|
|
11
|
+
// Zod 4 uses _zod property
|
|
12
|
+
if ("_zod" in val && "safeParse" in val) return true;
|
|
13
|
+
// Zod 3 uses _def property
|
|
14
|
+
if ("_def" in val && "safeParse" in val) return true;
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get the Zod type name from a schema (works with both Zod 3 and 4)
|
|
20
|
+
*/
|
|
21
|
+
export function getZodTypeName(schema: unknown): string | undefined {
|
|
22
|
+
if (!isZodSchema(schema)) return undefined;
|
|
23
|
+
const s = schema as Record<string, unknown>;
|
|
24
|
+
// Zod 4: check _zod.def.typeName
|
|
25
|
+
if (s._zod && typeof s._zod === "object") {
|
|
26
|
+
const zod = s._zod as Record<string, unknown>;
|
|
27
|
+
if (zod.def && typeof zod.def === "object") {
|
|
28
|
+
const def = zod.def as Record<string, unknown>;
|
|
29
|
+
if (typeof def.typeName === "string") return def.typeName;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Zod 3: check _def.typeName
|
|
33
|
+
if (s._def && typeof s._def === "object") {
|
|
34
|
+
const def = s._def as Record<string, unknown>;
|
|
35
|
+
if (typeof def.typeName === "string") return def.typeName;
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if schema is a ZodObject (works with both Zod 3 and 4)
|
|
42
|
+
*/
|
|
43
|
+
export function isZodObject(schema: unknown): boolean {
|
|
44
|
+
const typeName = getZodTypeName(schema);
|
|
45
|
+
return typeName === "ZodObject" || typeName === "object";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if schema is a ZodOptional (works with both Zod 3 and 4)
|
|
50
|
+
*/
|
|
51
|
+
export function isZodOptional(schema: unknown): boolean {
|
|
52
|
+
const typeName = getZodTypeName(schema);
|
|
53
|
+
return typeName === "ZodOptional" || typeName === "optional";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if schema is a ZodNullable (works with both Zod 3 and 4)
|
|
58
|
+
*/
|
|
59
|
+
export function isZodNullable(schema: unknown): boolean {
|
|
60
|
+
const typeName = getZodTypeName(schema);
|
|
61
|
+
return typeName === "ZodNullable" || typeName === "nullable";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check if schema is a ZodArray (works with both Zod 3 and 4)
|
|
66
|
+
*/
|
|
67
|
+
export function isZodArray(schema: unknown): boolean {
|
|
68
|
+
const typeName = getZodTypeName(schema);
|
|
69
|
+
return typeName === "ZodArray" || typeName === "array";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if schema is a ZodDefault (works with both Zod 3 and 4)
|
|
74
|
+
*/
|
|
75
|
+
export function isZodDefault(schema: unknown): boolean {
|
|
76
|
+
const typeName = getZodTypeName(schema);
|
|
77
|
+
return typeName === "ZodDefault" || typeName === "default";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get the shape of a ZodObject schema (works with both Zod 3 and 4)
|
|
82
|
+
*/
|
|
83
|
+
export function getObjectShape(schema: unknown): Record<string, unknown> | undefined {
|
|
84
|
+
if (!isZodObject(schema)) return undefined;
|
|
85
|
+
const s = schema as Record<string, unknown>;
|
|
86
|
+
// Zod 4: shape is in _zod.def.shape
|
|
87
|
+
if (s._zod && typeof s._zod === "object") {
|
|
88
|
+
const zod = s._zod as Record<string, unknown>;
|
|
89
|
+
if (zod.def && typeof zod.def === "object") {
|
|
90
|
+
const def = zod.def as Record<string, unknown>;
|
|
91
|
+
if (def.shape && typeof def.shape === "object") {
|
|
92
|
+
return def.shape as Record<string, unknown>;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Zod 3: shape property directly on schema
|
|
97
|
+
if (typeof s.shape === "object" && s.shape !== null) {
|
|
98
|
+
return s.shape as Record<string, unknown>;
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get the inner schema from Optional/Nullable/Default (works with both Zod 3 and 4)
|
|
105
|
+
*/
|
|
106
|
+
export function getInnerSchema(schema: unknown): unknown {
|
|
107
|
+
const s = schema as Record<string, unknown>;
|
|
108
|
+
// Zod 4: innerType is in _zod.def.innerType
|
|
109
|
+
if (s._zod && typeof s._zod === "object") {
|
|
110
|
+
const zod = s._zod as Record<string, unknown>;
|
|
111
|
+
if (zod.def && typeof zod.def === "object") {
|
|
112
|
+
const def = zod.def as Record<string, unknown>;
|
|
113
|
+
if (def.innerType) return def.innerType;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Zod 3: unwrap method or _def.innerType
|
|
117
|
+
if (typeof s.unwrap === "function") {
|
|
118
|
+
return (s.unwrap as () => unknown)();
|
|
119
|
+
}
|
|
120
|
+
if (s._def && typeof s._def === "object") {
|
|
121
|
+
const def = s._def as Record<string, unknown>;
|
|
122
|
+
if (def.innerType) return def.innerType;
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get the element schema from a ZodArray (works with both Zod 3 and 4)
|
|
129
|
+
*/
|
|
130
|
+
export function getArrayElement(schema: unknown): unknown {
|
|
131
|
+
if (!isZodArray(schema)) return undefined;
|
|
132
|
+
const s = schema as Record<string, unknown>;
|
|
133
|
+
// Zod 4: element is in _zod.def.element
|
|
134
|
+
if (s._zod && typeof s._zod === "object") {
|
|
135
|
+
const zod = s._zod as Record<string, unknown>;
|
|
136
|
+
if (zod.def && typeof zod.def === "object") {
|
|
137
|
+
const def = zod.def as Record<string, unknown>;
|
|
138
|
+
if (def.element) return def.element;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Zod 3: element property directly on schema
|
|
142
|
+
if (s.element) return s.element;
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* ```ts
|
|
6
6
|
* import { defineOntology, fieldFrom } from 'ont-run';
|
|
7
7
|
* import { z } from 'zod';
|
|
8
|
+
* import { hello } from './resolvers/hello.js';
|
|
8
9
|
*
|
|
9
10
|
* export default defineOntology({
|
|
10
11
|
* name: 'my-api',
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
* access: ['public'],
|
|
24
25
|
* entities: [],
|
|
25
26
|
* inputs: z.object({ name: z.string() }),
|
|
26
|
-
* resolver:
|
|
27
|
+
* resolver: hello, // Direct function reference for type safety
|
|
27
28
|
* },
|
|
28
29
|
* },
|
|
29
30
|
* });
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
*/
|
|
32
33
|
|
|
33
34
|
// Main API
|
|
34
|
-
export { defineOntology } from "./config/define.js";
|
|
35
|
+
export { defineOntology, defineFunction } from "./config/define.js";
|
|
35
36
|
export { fieldFrom, userContext } from "./config/categorical.js";
|
|
36
37
|
export { startOnt } from "./server/start.js";
|
|
37
38
|
export type { StartOntOptions, StartOntResult } from "./server/start.js";
|
package/src/lockfile/hasher.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { createHash } from "crypto";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
4
3
|
import type { OntologyConfig } from "../config/types.js";
|
|
5
4
|
import { getFieldFromMetadata, getUserContextFields } from "../config/categorical.js";
|
|
5
|
+
import {
|
|
6
|
+
isZodObject,
|
|
7
|
+
isZodOptional,
|
|
8
|
+
isZodNullable,
|
|
9
|
+
isZodArray,
|
|
10
|
+
isZodDefault,
|
|
11
|
+
getObjectShape,
|
|
12
|
+
getInnerSchema,
|
|
13
|
+
getArrayElement,
|
|
14
|
+
} from "../config/zod-utils.js";
|
|
6
15
|
import type {
|
|
7
16
|
OntologySnapshot,
|
|
8
17
|
FunctionShape,
|
|
@@ -28,34 +37,48 @@ function extractFieldReferences(
|
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
// Handle ZodObject - recurse into properties
|
|
31
|
-
if (schema
|
|
32
|
-
const shape = schema
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
if (isZodObject(schema)) {
|
|
41
|
+
const shape = getObjectShape(schema);
|
|
42
|
+
if (shape) {
|
|
43
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
44
|
+
const fieldPath = path ? `${path}.${key}` : key;
|
|
45
|
+
results.push(
|
|
46
|
+
...extractFieldReferences(value as z.ZodType<unknown>, fieldPath)
|
|
47
|
+
);
|
|
48
|
+
}
|
|
38
49
|
}
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
// Handle ZodOptional - unwrap
|
|
42
|
-
if (schema
|
|
43
|
-
|
|
53
|
+
if (isZodOptional(schema)) {
|
|
54
|
+
const inner = getInnerSchema(schema);
|
|
55
|
+
if (inner) {
|
|
56
|
+
results.push(...extractFieldReferences(inner as z.ZodType<unknown>, path));
|
|
57
|
+
}
|
|
44
58
|
}
|
|
45
59
|
|
|
46
60
|
// Handle ZodNullable - unwrap
|
|
47
|
-
if (schema
|
|
48
|
-
|
|
61
|
+
if (isZodNullable(schema)) {
|
|
62
|
+
const inner = getInnerSchema(schema);
|
|
63
|
+
if (inner) {
|
|
64
|
+
results.push(...extractFieldReferences(inner as z.ZodType<unknown>, path));
|
|
65
|
+
}
|
|
49
66
|
}
|
|
50
67
|
|
|
51
68
|
// Handle ZodArray - recurse into element
|
|
52
|
-
if (schema
|
|
53
|
-
|
|
69
|
+
if (isZodArray(schema)) {
|
|
70
|
+
const element = getArrayElement(schema);
|
|
71
|
+
if (element) {
|
|
72
|
+
results.push(...extractFieldReferences(element as z.ZodType<unknown>, `${path}[]`));
|
|
73
|
+
}
|
|
54
74
|
}
|
|
55
75
|
|
|
56
76
|
// Handle ZodDefault - unwrap
|
|
57
|
-
if (schema
|
|
58
|
-
|
|
77
|
+
if (isZodDefault(schema)) {
|
|
78
|
+
const inner = getInnerSchema(schema);
|
|
79
|
+
if (inner) {
|
|
80
|
+
results.push(...extractFieldReferences(inner as z.ZodType<unknown>, path));
|
|
81
|
+
}
|
|
59
82
|
}
|
|
60
83
|
|
|
61
84
|
return results;
|
|
@@ -85,14 +108,14 @@ export function extractOntology(config: OntologyConfig): OntologySnapshot {
|
|
|
85
108
|
// This ensures we're comparing the shape, not the Zod instance
|
|
86
109
|
let inputsSchema: Record<string, unknown>;
|
|
87
110
|
try {
|
|
88
|
-
inputsSchema =
|
|
89
|
-
|
|
90
|
-
|
|
111
|
+
inputsSchema = z.toJSONSchema(fn.inputs, {
|
|
112
|
+
reused: "inline",
|
|
113
|
+
unrepresentable: "any",
|
|
91
114
|
}) as Record<string, unknown>;
|
|
92
115
|
// Remove $schema key if present
|
|
93
116
|
delete inputsSchema.$schema;
|
|
94
117
|
} catch {
|
|
95
|
-
// If
|
|
118
|
+
// If z.toJSONSchema fails, use a placeholder
|
|
96
119
|
inputsSchema = { type: "unknown" };
|
|
97
120
|
}
|
|
98
121
|
|
|
@@ -100,8 +123,9 @@ export function extractOntology(config: OntologyConfig): OntologySnapshot {
|
|
|
100
123
|
let outputsSchema: Record<string, unknown> | undefined;
|
|
101
124
|
if (fn.outputs) {
|
|
102
125
|
try {
|
|
103
|
-
outputsSchema =
|
|
104
|
-
|
|
126
|
+
outputsSchema = z.toJSONSchema(fn.outputs, {
|
|
127
|
+
reused: "inline",
|
|
128
|
+
unrepresentable: "any",
|
|
105
129
|
}) as Record<string, unknown>;
|
|
106
130
|
delete outputsSchema.$schema;
|
|
107
131
|
} catch {
|
package/src/server/api/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { cors } from "hono/cors";
|
|
3
|
-
import consola from "consola";
|
|
4
3
|
import type { OntologyConfig } from "../../config/types.js";
|
|
5
4
|
import {
|
|
6
5
|
createAuthMiddleware,
|
|
@@ -9,13 +8,10 @@ import {
|
|
|
9
8
|
type OntologyVariables,
|
|
10
9
|
} from "./middleware.js";
|
|
11
10
|
import { createApiRoutes, getFunctionsInfo } from "./router.js";
|
|
12
|
-
import { findMissingResolvers } from "../resolver.js";
|
|
13
11
|
|
|
14
12
|
export interface ApiServerOptions {
|
|
15
13
|
/** The ontology configuration */
|
|
16
14
|
config: OntologyConfig;
|
|
17
|
-
/** Directory containing the ontology.config.ts (for resolving resolver paths) */
|
|
18
|
-
configDir: string;
|
|
19
15
|
/** Environment to use (e.g., 'dev', 'prod') */
|
|
20
16
|
env: string;
|
|
21
17
|
/** Enable CORS (default: true) */
|
|
@@ -26,7 +22,7 @@ export interface ApiServerOptions {
|
|
|
26
22
|
* Create the Hono API app from an OntologyConfig
|
|
27
23
|
*/
|
|
28
24
|
export function createApiApp(options: ApiServerOptions): Hono<{ Variables: OntologyVariables }> {
|
|
29
|
-
const { config,
|
|
25
|
+
const { config, env, cors: enableCors = true } = options;
|
|
30
26
|
|
|
31
27
|
// Get environment config
|
|
32
28
|
const envConfig = config.environments[env];
|
|
@@ -36,15 +32,6 @@ export function createApiApp(options: ApiServerOptions): Hono<{ Variables: Ontol
|
|
|
36
32
|
);
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
// Check for missing resolvers
|
|
40
|
-
const missingResolvers = findMissingResolvers(config, configDir);
|
|
41
|
-
if (missingResolvers.length > 0) {
|
|
42
|
-
consola.warn(`Missing resolvers (${missingResolvers.length}):`);
|
|
43
|
-
for (const resolver of missingResolvers) {
|
|
44
|
-
consola.warn(` - ${resolver}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
35
|
const app = new Hono<{ Variables: OntologyVariables }>();
|
|
49
36
|
|
|
50
37
|
// Global middleware
|
|
@@ -83,7 +70,7 @@ export function createApiApp(options: ApiServerOptions): Hono<{ Variables: Ontol
|
|
|
83
70
|
});
|
|
84
71
|
|
|
85
72
|
// Mount function routes under /api
|
|
86
|
-
const apiRoutes = createApiRoutes(config
|
|
73
|
+
const apiRoutes = createApiRoutes(config);
|
|
87
74
|
app.route("/api", apiRoutes);
|
|
88
75
|
|
|
89
76
|
return app;
|
package/src/server/api/router.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import type { OntologyConfig, FunctionDefinition } from "../../config/types.js";
|
|
3
|
-
import { loadResolver } from "../resolver.js";
|
|
4
3
|
import { getUserContextFields } from "../../config/categorical.js";
|
|
5
4
|
import {
|
|
6
5
|
createAccessControlMiddleware,
|
|
@@ -11,8 +10,7 @@ import {
|
|
|
11
10
|
* Create API routes from function definitions
|
|
12
11
|
*/
|
|
13
12
|
export function createApiRoutes(
|
|
14
|
-
config: OntologyConfig
|
|
15
|
-
configDir: string
|
|
13
|
+
config: OntologyConfig
|
|
16
14
|
): Hono<{ Variables: OntologyVariables }> {
|
|
17
15
|
const router = new Hono<{ Variables: OntologyVariables }>();
|
|
18
16
|
|
|
@@ -83,10 +81,9 @@ export function createApiRoutes(
|
|
|
83
81
|
args = parsed.data;
|
|
84
82
|
}
|
|
85
83
|
|
|
86
|
-
//
|
|
84
|
+
// Execute resolver
|
|
87
85
|
try {
|
|
88
|
-
const
|
|
89
|
-
const result = await resolver(resolverContext, args);
|
|
86
|
+
const result = await fn.resolver(resolverContext, args);
|
|
90
87
|
return c.json(result);
|
|
91
88
|
} catch (error) {
|
|
92
89
|
console.error(`Error in resolver ${name}:`, error);
|
package/src/server/mcp/index.ts
CHANGED
|
@@ -39,8 +39,6 @@ function getAuthResult(authInfo?: AuthInfo): AuthResult {
|
|
|
39
39
|
export interface McpServerOptions {
|
|
40
40
|
/** The ontology configuration */
|
|
41
41
|
config: OntologyConfig;
|
|
42
|
-
/** Directory containing the ontology.config.ts */
|
|
43
|
-
configDir: string;
|
|
44
42
|
/** Environment to use */
|
|
45
43
|
env: string;
|
|
46
44
|
/** Port for the MCP HTTP server */
|
|
@@ -51,7 +49,7 @@ export interface McpServerOptions {
|
|
|
51
49
|
* Create the MCP server instance with per-request authentication
|
|
52
50
|
*/
|
|
53
51
|
export function createMcpServer(options: McpServerOptions): Server {
|
|
54
|
-
const { config,
|
|
52
|
+
const { config, env } = options;
|
|
55
53
|
|
|
56
54
|
// Get environment config
|
|
57
55
|
const envConfig = config.environments[env];
|
|
@@ -67,7 +65,7 @@ export function createMcpServer(options: McpServerOptions): Server {
|
|
|
67
65
|
const allTools = generateMcpTools(config);
|
|
68
66
|
|
|
69
67
|
// Create tool executor factory that accepts per-request access groups
|
|
70
|
-
const executeToolWithAccess = createToolExecutor(config,
|
|
68
|
+
const executeToolWithAccess = createToolExecutor(config, env, envConfig, logger);
|
|
71
69
|
|
|
72
70
|
// Create MCP server
|
|
73
71
|
const server = new Server(
|
package/src/server/mcp/tools.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
3
2
|
import type {
|
|
4
3
|
OntologyConfig,
|
|
5
4
|
FunctionDefinition,
|
|
@@ -8,7 +7,17 @@ import type {
|
|
|
8
7
|
AuthResult,
|
|
9
8
|
} from "../../config/types.js";
|
|
10
9
|
import { getFieldFromMetadata, getUserContextFields, hasUserContextMetadata } from "../../config/categorical.js";
|
|
11
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
isZodObject,
|
|
12
|
+
isZodOptional,
|
|
13
|
+
isZodNullable,
|
|
14
|
+
isZodArray,
|
|
15
|
+
isZodDefault,
|
|
16
|
+
getObjectShape,
|
|
17
|
+
getInnerSchema,
|
|
18
|
+
getArrayElement,
|
|
19
|
+
} from "../../config/zod-utils.js";
|
|
20
|
+
import type { Logger } from "../resolver.js";
|
|
12
21
|
|
|
13
22
|
/**
|
|
14
23
|
* Field reference info for MCP tools
|
|
@@ -94,36 +103,48 @@ function extractFieldReferencesForMcp(
|
|
|
94
103
|
}
|
|
95
104
|
|
|
96
105
|
// Handle ZodObject - recurse into properties
|
|
97
|
-
if (schema
|
|
98
|
-
const shape = schema
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
if (isZodObject(schema)) {
|
|
107
|
+
const shape = getObjectShape(schema);
|
|
108
|
+
if (shape) {
|
|
109
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
110
|
+
const fieldPath = path ? `${path}.${key}` : key;
|
|
111
|
+
results.push(
|
|
112
|
+
...extractFieldReferencesForMcp(value as z.ZodType<unknown>, fieldPath)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
104
115
|
}
|
|
105
116
|
}
|
|
106
117
|
|
|
107
118
|
// Handle ZodOptional - unwrap
|
|
108
|
-
if (schema
|
|
109
|
-
|
|
119
|
+
if (isZodOptional(schema)) {
|
|
120
|
+
const inner = getInnerSchema(schema);
|
|
121
|
+
if (inner) {
|
|
122
|
+
results.push(...extractFieldReferencesForMcp(inner as z.ZodType<unknown>, path));
|
|
123
|
+
}
|
|
110
124
|
}
|
|
111
125
|
|
|
112
126
|
// Handle ZodNullable - unwrap
|
|
113
|
-
if (schema
|
|
114
|
-
|
|
127
|
+
if (isZodNullable(schema)) {
|
|
128
|
+
const inner = getInnerSchema(schema);
|
|
129
|
+
if (inner) {
|
|
130
|
+
results.push(...extractFieldReferencesForMcp(inner as z.ZodType<unknown>, path));
|
|
131
|
+
}
|
|
115
132
|
}
|
|
116
133
|
|
|
117
134
|
// Handle ZodArray - recurse into element
|
|
118
|
-
if (schema
|
|
119
|
-
|
|
135
|
+
if (isZodArray(schema)) {
|
|
136
|
+
const element = getArrayElement(schema);
|
|
137
|
+
if (element) {
|
|
138
|
+
results.push(...extractFieldReferencesForMcp(element as z.ZodType<unknown>, `${path}[]`));
|
|
139
|
+
}
|
|
120
140
|
}
|
|
121
141
|
|
|
122
142
|
// Handle ZodDefault - unwrap
|
|
123
|
-
if (schema
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
143
|
+
if (isZodDefault(schema)) {
|
|
144
|
+
const inner = getInnerSchema(schema);
|
|
145
|
+
if (inner) {
|
|
146
|
+
results.push(...extractFieldReferencesForMcp(inner as z.ZodType<unknown>, path));
|
|
147
|
+
}
|
|
127
148
|
}
|
|
128
149
|
|
|
129
150
|
return results;
|
|
@@ -139,8 +160,9 @@ export function generateMcpTools(config: OntologyConfig): McpTool[] {
|
|
|
139
160
|
// Convert Zod schema to JSON Schema for MCP
|
|
140
161
|
let inputSchema: Record<string, unknown>;
|
|
141
162
|
try {
|
|
142
|
-
inputSchema =
|
|
143
|
-
|
|
163
|
+
inputSchema = z.toJSONSchema(fn.inputs, {
|
|
164
|
+
reused: "inline",
|
|
165
|
+
unrepresentable: "any",
|
|
144
166
|
}) as Record<string, unknown>;
|
|
145
167
|
// Remove $schema key if present
|
|
146
168
|
delete inputSchema.$schema;
|
|
@@ -154,8 +176,9 @@ export function generateMcpTools(config: OntologyConfig): McpTool[] {
|
|
|
154
176
|
let outputSchema: Record<string, unknown> | undefined;
|
|
155
177
|
if (fn.outputs) {
|
|
156
178
|
try {
|
|
157
|
-
outputSchema =
|
|
158
|
-
|
|
179
|
+
outputSchema = z.toJSONSchema(fn.outputs, {
|
|
180
|
+
reused: "inline",
|
|
181
|
+
unrepresentable: "any",
|
|
159
182
|
}) as Record<string, unknown>;
|
|
160
183
|
delete outputSchema.$schema;
|
|
161
184
|
} catch {
|
|
@@ -198,7 +221,6 @@ export function filterToolsByAccess(
|
|
|
198
221
|
*/
|
|
199
222
|
export function createToolExecutor(
|
|
200
223
|
config: OntologyConfig,
|
|
201
|
-
configDir: string,
|
|
202
224
|
env: string,
|
|
203
225
|
envConfig: EnvironmentConfig,
|
|
204
226
|
logger: Logger
|
|
@@ -253,8 +275,7 @@ export function createToolExecutor(
|
|
|
253
275
|
accessGroups: authResult.groups,
|
|
254
276
|
};
|
|
255
277
|
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
return resolver(resolverContext, parsed.data);
|
|
278
|
+
// Execute resolver
|
|
279
|
+
return fn.resolver(resolverContext, parsed.data);
|
|
259
280
|
};
|
|
260
281
|
}
|