@okfit/mcp 0.6.4 → 0.6.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/index.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import { Remediation } from "@effected/engine";
2
2
  import { FileSystem, Layer, Path, Schema, Stdio } from "effect";
3
3
  import { AppDirs, Xdg } from "@effected/xdg";
4
- import { LoadedConcept } from "@okfit/core";
5
- import { Distribution } from "@okfit/engine";
4
+ import { ConceptSummary, Distribution, toConceptSummary } from "@okfit/engine";
6
5
  import { ChildProcessSpawner } from "effect/process";
7
6
  import { Toolkit } from "effect/ai";
8
7
  //#region src/errors.d.ts
@@ -144,22 +143,6 @@ export declare const ConceptResources: (projectRoot: string) => Layer.Layer<neve
144
143
  */
145
144
  export declare const IndexResource: (projectRoot: string) => import("effect/Layer").Layer<never, never, import("@effected/xdg").AppDirs | FileSystem.FileSystem | Path.Path | import("@effected/xdg").Xdg>;
146
145
  //#endregion
147
- //#region src/schema/ConceptSummary.d.ts
148
- /** The seven-field shape every list-like tool result carries (N-16). @public */
149
- export declare const ConceptSummary: Schema.Struct<{
150
- readonly id: Schema.String;
151
- readonly type: Schema.String;
152
- readonly title: Schema.String;
153
- readonly description: Schema.NullOr<Schema.String>;
154
- readonly status: Schema.Literals<readonly ["draft", "stable", "deprecated"]>;
155
- readonly tags: Schema.$Array<Schema.String>;
156
- readonly path: Schema.String;
157
- }>;
158
- /** @public */
159
- export type ConceptSummary = typeof ConceptSummary.Type;
160
- /** Project one loaded concept into its summary. @public */
161
- export declare const toConceptSummary: (concept: LoadedConcept) => ConceptSummary;
162
- //#endregion
163
146
  //#region src/schema/tools.d.ts
164
147
  /** `describe_vocabulary`'s result (§5.2). @public */
165
148
  export declare const DescribeVocabularySuccess: Schema.Struct<{
@@ -789,5 +772,5 @@ export declare const ToolsLayer: (projectRoot: string, distribution?: Distributi
789
772
  */
790
773
  export declare const MCP_VERSION: string;
791
774
  //#endregion
792
- export { type PlatformServices, Remediation, type ServerOptions };
775
+ export { ConceptSummary, type PlatformServices, Remediation, type ServerOptions, toConceptSummary };
793
776
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,39 @@
1
+ import { ConceptNotFound, InvalidArgument } from "../errors.js";
2
+ import { ToolFailure } from "@effected/mcp";
3
+ import { Effect } from "effect";
4
+
5
+ //#region src/internal/conceptNotFound.ts
6
+ /**
7
+ * Maps the engine's `QueryConceptNotFoundError` to the tool-facing failure
8
+ * `get_concept` and `concept_neighbors` share: `InvalidArgument` for an empty
9
+ * id, `ConceptNotFound` otherwise. `message` is composed through
10
+ * `ToolFailure.message` at construction, since a declared typed failure under
11
+ * `failureMode: "error"` reaches the wire as `error.message` alone.
12
+ *
13
+ * @internal
14
+ */
15
+ const toConceptLookupFailure = (id, error) => {
16
+ if (error.reason === "empty-id") {
17
+ const remediation = {
18
+ hint: "Pass a bundle-relative concept id such as decisions/cli-exit-codes.",
19
+ suggestedTool: "list_concepts"
20
+ };
21
+ return Effect.fail(new InvalidArgument({
22
+ argument: "id",
23
+ message: ToolFailure.message("id must not be empty", remediation),
24
+ remediation
25
+ }));
26
+ }
27
+ const remediation = {
28
+ hint: "Call list_concepts to see the ids this bundle contains.",
29
+ suggestedTool: "list_concepts"
30
+ };
31
+ return Effect.fail(new ConceptNotFound({
32
+ id,
33
+ message: ToolFailure.message(`no concept "${ToolFailure.truncate(id)}" in this bundle`, remediation),
34
+ remediation
35
+ }));
36
+ };
37
+
38
+ //#endregion
39
+ export { toConceptLookupFailure };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okfit/mcp",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "private": false,
5
5
  "description": "Model Context Protocol server for okfit: query and understand Open Knowledge Format (OKF) bundles from an agent.",
6
6
  "keywords": [
@@ -54,11 +54,11 @@
54
54
  "@effected/mcp": "^0.3.0",
55
55
  "@effected/schemastore": "^0.17.0",
56
56
  "@effected/toml": "^0.10.0",
57
- "@effected/walker": "^0.14.0",
58
- "@effected/xdg": "^0.8.1",
57
+ "@effected/walker": "^0.14.1",
58
+ "@effected/xdg": "^0.8.2",
59
59
  "@effected/yaml": "^0.18.0",
60
60
  "@okfit/core": "0.8.3",
61
- "@okfit/engine": "0.9.3",
61
+ "@okfit/engine": "0.10.0",
62
62
  "@okfit/profiles": "0.8.2",
63
63
  "effect": "4.0.0-rc.118"
64
64
  },
@@ -1,27 +1,3 @@
1
- import { Schema } from "effect";
2
- import { Derive, Status } from "@okfit/core";
1
+ import { ConceptSummary, toConceptSummary } from "@okfit/engine";
3
2
 
4
- //#region src/schema/ConceptSummary.ts
5
- /** The seven-field shape every list-like tool result carries (N-16). @public */
6
- const ConceptSummary = Schema.Struct({
7
- id: Schema.String,
8
- type: Schema.String,
9
- title: Schema.String,
10
- description: Schema.NullOr(Schema.String),
11
- status: Status,
12
- tags: Schema.Array(Schema.String),
13
- path: Schema.String
14
- });
15
- /** Project one loaded concept into its summary. @public */
16
- const toConceptSummary = (concept) => ({
17
- id: concept.id,
18
- type: concept.frontmatter.type,
19
- title: Derive.title(concept),
20
- description: concept.frontmatter.description ?? null,
21
- status: Derive.status(concept.frontmatter),
22
- tags: [...concept.frontmatter.tags ?? []],
23
- path: concept.path
24
- });
25
-
26
- //#endregion
27
3
  export { ConceptSummary, toConceptSummary };
@@ -1,11 +1,11 @@
1
- import { ConceptNotFound, InvalidArgument, McpToolError } from "../errors.js";
1
+ import { McpToolError } from "../errors.js";
2
2
  import { loadToolContext } from "../internal/toolContext.js";
3
3
  import { toConceptSummary } from "../schema/ConceptSummary.js";
4
4
  import { ConceptNeighborsSuccess } from "../schema/tools.js";
5
- import { ToolFailure } from "@effected/mcp";
6
- import { Effect, FileSystem, Option, Path, Schema } from "effect";
7
- import { ConceptId, Graph } from "@okfit/core";
5
+ import { toConceptLookupFailure } from "../internal/conceptNotFound.js";
6
+ import { Effect, FileSystem, Path, Schema } from "effect";
8
7
  import { Tool } from "effect/ai";
8
+ import { ConceptQuery } from "@okfit/engine";
9
9
  import { AppDirs, Xdg } from "@effected/xdg";
10
10
 
11
11
  //#region src/tools/conceptNeighbors.ts
@@ -40,47 +40,15 @@ const conceptNeighbors = Tool.make("concept_neighbors", {
40
40
  */
41
41
  const handleConceptNeighbors = (projectRoot, params) => Effect.gen(function* () {
42
42
  const ctx = yield* loadToolContext(projectRoot);
43
- const normalized = ConceptId.normalize(params.id);
44
- if (Option.isNone(normalized)) {
45
- const remediation = {
46
- hint: "Pass a bundle-relative concept id such as decisions/cli-exit-codes.",
47
- suggestedTool: "list_concepts"
48
- };
49
- return yield* Effect.fail(new InvalidArgument({
50
- argument: "id",
51
- message: ToolFailure.message("id must not be empty", remediation),
52
- remediation
53
- }));
54
- }
55
- const id = normalized.value;
56
- if (!ctx.bundle.concepts.has(id)) {
57
- const remediation = {
58
- hint: "Call list_concepts to see the ids this bundle contains.",
59
- suggestedTool: "list_concepts"
60
- };
61
- return yield* Effect.fail(new ConceptNotFound({
62
- id: params.id,
63
- message: ToolFailure.message(`no concept "${ToolFailure.truncate(params.id)}" in this bundle`, remediation),
64
- remediation
65
- }));
66
- }
67
- const graph = Graph.fromBundle(ctx.bundle);
68
- const project = (node) => {
69
- if (node.kind !== "concept") return {
70
- id: node.id,
71
- kind: node.kind,
72
- summary: null
73
- };
74
- const concept = ctx.bundle.concepts.get(node.id);
75
- return {
76
- id: node.id,
77
- kind: node.kind,
78
- summary: concept === void 0 ? null : toConceptSummary(concept)
79
- };
80
- };
43
+ const result = yield* ConceptQuery.neighbors(ctx.bundle, params.id).pipe(Effect.catchTag("QueryConceptNotFoundError", (e) => toConceptLookupFailure(params.id, e)));
44
+ const project = (neighbor) => ({
45
+ id: neighbor.id,
46
+ kind: neighbor.kind,
47
+ summary: neighbor.concept === null ? null : toConceptSummary(neighbor.concept)
48
+ });
81
49
  return {
82
- outgoing: graph.successors(id).map(project),
83
- incoming: graph.predecessors(id).map(project)
50
+ outgoing: result.outgoing.map(project),
51
+ incoming: result.incoming.map(project)
84
52
  };
85
53
  });
86
54
 
@@ -1,10 +1,11 @@
1
- import { ConceptNotFound, InvalidArgument, McpToolError } from "../errors.js";
1
+ import { McpToolError } from "../errors.js";
2
2
  import { loadToolContext } from "../internal/toolContext.js";
3
3
  import { GetConceptSuccess } from "../schema/tools.js";
4
- import { ToolFailure } from "@effected/mcp";
5
- import { Effect, FileSystem, Option, Path, Schema } from "effect";
6
- import { ConceptId, Derive, Graph } from "@okfit/core";
4
+ import { toConceptLookupFailure } from "../internal/conceptNotFound.js";
5
+ import { Effect, FileSystem, Path, Schema } from "effect";
6
+ import { Derive } from "@okfit/core";
7
7
  import { Tool } from "effect/ai";
8
+ import { ConceptQuery } from "@okfit/engine";
8
9
  import { AppDirs, Xdg } from "@effected/xdg";
9
10
 
10
11
  //#region src/tools/getConcept.ts
@@ -41,43 +42,9 @@ const getConcept = Tool.make("get_concept", {
41
42
  */
42
43
  const handleGetConcept = (projectRoot, params) => Effect.gen(function* () {
43
44
  const ctx = yield* loadToolContext(projectRoot);
44
- const normalized = ConceptId.normalize(params.id);
45
- if (Option.isNone(normalized)) {
46
- const remediation = {
47
- hint: "Pass a bundle-relative concept id such as decisions/cli-exit-codes.",
48
- suggestedTool: "list_concepts"
49
- };
50
- return yield* Effect.fail(new InvalidArgument({
51
- argument: "id",
52
- message: ToolFailure.message("id must not be empty", remediation),
53
- remediation
54
- }));
55
- }
56
- const id = normalized.value;
57
- const concept = ctx.bundle.concepts.get(id);
58
- if (concept === void 0) {
59
- const remediation = {
60
- hint: "Call list_concepts to see the ids this bundle contains.",
61
- suggestedTool: "list_concepts"
62
- };
63
- return yield* Effect.fail(new ConceptNotFound({
64
- id: params.id,
65
- message: ToolFailure.message(`no concept "${ToolFailure.truncate(params.id)}" in this bundle`, remediation),
66
- remediation
67
- }));
68
- }
69
- const graph = Graph.fromBundle(ctx.bundle);
70
- const links = graph.edges.filter((edge) => edge.from === id).map((edge) => ({
71
- to: edge.to,
72
- kind: Option.match(graph.node(edge.to), {
73
- onNone: () => "missing",
74
- onSome: (node) => node.kind
75
- }),
76
- source: edge.data.source,
77
- ...edge.data.field === void 0 ? {} : { field: edge.data.field }
78
- }));
45
+ const { concept, links } = yield* ConceptQuery.get(ctx.bundle, params.id).pipe(Effect.catchTag("QueryConceptNotFoundError", (e) => toConceptLookupFailure(params.id, e)));
79
46
  return {
80
- id,
47
+ id: concept.id,
81
48
  type: concept.frontmatter.type,
82
49
  title: Derive.title(concept),
83
50
  description: concept.frontmatter.description ?? null,
@@ -4,8 +4,8 @@ import { toConceptSummary } from "../schema/ConceptSummary.js";
4
4
  import { ListConceptsParams, ListConceptsSuccess } from "../schema/tools.js";
5
5
  import { ToolFailure } from "@effected/mcp";
6
6
  import { Effect, FileSystem, Path } from "effect";
7
- import { Derive } from "@okfit/core";
8
7
  import { Tool } from "effect/ai";
8
+ import { ConceptQuery } from "@okfit/engine";
9
9
  import { AppDirs, Xdg } from "@effected/xdg";
10
10
 
11
11
  //#region src/tools/listConcepts.ts
@@ -58,18 +58,11 @@ const unknown = (kind, requested, valid) => {
58
58
  /** @public */
59
59
  const handleListConcepts = (projectRoot, params) => Effect.gen(function* () {
60
60
  const ctx = yield* loadToolContext(projectRoot);
61
- const declaredTypes = Object.keys(ctx.config.types ?? {}).toSorted();
62
- const declaredTags = Object.keys(ctx.config.tags ?? {}).toSorted();
63
- if (params.type !== void 0 && !declaredTypes.includes(params.type)) return yield* Effect.fail(unknown("type", params.type, declaredTypes));
64
- const requestedTags = params.tags ?? [];
65
- for (const tag of requestedTags) if (!declaredTags.includes(tag)) return yield* Effect.fail(unknown("tag", tag, declaredTags));
66
- const filtered = [...ctx.bundle.concepts.values()].filter((concept) => {
67
- if (params.type !== void 0 && concept.frontmatter.type !== params.type) return false;
68
- const tags = concept.frontmatter.tags ?? [];
69
- if (!requestedTags.every((tag) => tags.includes(tag))) return false;
70
- if (params.status !== void 0 && Derive.status(concept.frontmatter) !== params.status) return false;
71
- return true;
72
- }).toSorted((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
61
+ const filtered = yield* ConceptQuery.list(ctx.bundle, ctx.config, {
62
+ ...params.type === void 0 ? {} : { types: [params.type] },
63
+ ...params.tags === void 0 ? {} : { tags: params.tags },
64
+ ...params.status === void 0 ? {} : { statuses: [params.status] }
65
+ }).pipe(Effect.catchTag("QueryUnknownVocabularyError", (e) => Effect.fail(unknown(e.kind, e.requested, e.valid))));
73
66
  const limit = params.limit ?? 200;
74
67
  const offset = params.offset ?? 0;
75
68
  return {
package/version.js CHANGED
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @public
7
7
  */
8
- const MCP_VERSION = "0.6.4";
8
+ const MCP_VERSION = "0.6.6";
9
9
 
10
10
  //#endregion
11
11
  export { MCP_VERSION };