@frigear-nu/mcp 0.0.0-stage → 0.0.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/dist/module.d.mts +17 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +34 -0
- package/dist/runtime/server/plugins/mcp.d.ts +23 -0
- package/dist/runtime/server/plugins/mcp.js +31 -0
- package/dist/runtime/server/utils/define.d.ts +46 -0
- package/dist/runtime/server/utils/define.js +90 -0
- package/dist/runtime/utils/access.d.ts +33 -0
- package/dist/runtime/utils/access.js +25 -0
- package/dist/runtime/utils/build.d.ts +23 -0
- package/dist/runtime/utils/build.js +10 -0
- package/dist/runtime/utils/index.d.ts +4 -0
- package/dist/runtime/utils/index.js +2 -0
- package/dist/types.d.mts +5 -0
- package/package.json +75 -3
- package/README.md +0 -3
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
export { addMcpDefinitions } from '../dist/runtime/utils/build.js';
|
|
3
|
+
|
|
4
|
+
interface ModuleOptions {
|
|
5
|
+
/** Route of the MCP endpoint. */
|
|
6
|
+
route?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare module 'nuxt/schema' {
|
|
10
|
+
interface NuxtConfig {
|
|
11
|
+
mcpModules?: ModuleOptions;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
15
|
+
|
|
16
|
+
export { _default as default };
|
|
17
|
+
export type { ModuleOptions };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver, addServerImportsDir, addServerPlugin } from '@nuxt/kit';
|
|
2
|
+
export { addMcpDefinitions } from '../dist/runtime/utils/build.js';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_INSTRUCTIONS = [
|
|
5
|
+
"You act as the signed-in user, limited to the scopes of the API token in use. You only see the tools that token and the user are allowed to use.",
|
|
6
|
+
'Personal data of other people (names, emails, form answers) is withheld unless the token carries the matching "*:pii" scope. Responses say when something was withheld; report that to the user instead of looking for another way to get it.',
|
|
7
|
+
"Some settings are deliberately not available here, for example where form submissions are delivered, because changing them could be used to exfiltrate data. When a tool refuses for that reason, tell the user to make the change in the admin UI."
|
|
8
|
+
].join("\n");
|
|
9
|
+
const module$1 = defineNuxtModule({
|
|
10
|
+
// Not `mcp`: that key belongs to @nuxtjs/mcp-toolkit.
|
|
11
|
+
meta: { name: "@frigear-nu/mcp", configKey: "mcpModules" },
|
|
12
|
+
defaults: {},
|
|
13
|
+
moduleDependencies: {
|
|
14
|
+
"@frigear-nu/auth": { version: ">=0.0.1" },
|
|
15
|
+
"@nuxtjs/mcp-toolkit": {
|
|
16
|
+
version: ">=0.24.0",
|
|
17
|
+
defaults: {}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
setup(options, nuxt) {
|
|
21
|
+
const resolver = createResolver(import.meta.url);
|
|
22
|
+
nuxt.options.nitro.experimental = { ...nuxt.options.nitro.experimental, asyncContext: true };
|
|
23
|
+
const host = nuxt.options;
|
|
24
|
+
host.mcp = {
|
|
25
|
+
...host.mcp,
|
|
26
|
+
...options.route ? { route: options.route } : {},
|
|
27
|
+
instructions: host.mcp?.instructions ?? DEFAULT_INSTRUCTIONS
|
|
28
|
+
};
|
|
29
|
+
addServerImportsDir(resolver.resolve("./runtime/server/utils"));
|
|
30
|
+
addServerPlugin(resolver.resolve("./runtime/server/plugins/mcp"));
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import type { CallToolResult, McpResolvedConfig } from '@nuxtjs/mcp-toolkit/server';
|
|
3
|
+
declare module 'nitropack' {
|
|
4
|
+
interface NitroRuntimeHooks {
|
|
5
|
+
'mcp:config:resolved': (ctx: {
|
|
6
|
+
config: McpResolvedConfig;
|
|
7
|
+
event: H3Event;
|
|
8
|
+
}) => void | Promise<void>;
|
|
9
|
+
'mcp:tool:called': (ctx: {
|
|
10
|
+
name: string;
|
|
11
|
+
result: CallToolResult;
|
|
12
|
+
durationMs: number;
|
|
13
|
+
event: H3Event;
|
|
14
|
+
}) => void | Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Defence in depth around the scoped definers:
|
|
19
|
+
* - fail closed: a definition that was not created through `defineScopedMcp*` (no marker) is dropped;
|
|
20
|
+
* - audit: who called which tool, never the arguments or the result.
|
|
21
|
+
*/
|
|
22
|
+
declare const _default: import("nitropack").NitroAppPlugin;
|
|
23
|
+
export default _default;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineNitroPlugin } from "nitropack/runtime";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { SCOPED_META_KEY } from "../../utils/access.js";
|
|
4
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
5
|
+
nitroApp.hooks.hook("mcp:config:resolved", ({ config }) => {
|
|
6
|
+
const scoped = (definition) => definition._meta?.[SCOPED_META_KEY] === true;
|
|
7
|
+
const serializable = (tool) => {
|
|
8
|
+
if (!tool.inputSchema) return true;
|
|
9
|
+
try {
|
|
10
|
+
z.toJSONSchema(z.object(tool.inputSchema), { io: "input" });
|
|
11
|
+
return true;
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.error(`[mcp] dropping tool "${tool.name}": its input schema is not JSON Schema compatible`, error);
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
config.tools = config.tools.filter(scoped).filter(serializable);
|
|
18
|
+
config.resources = config.resources.filter(scoped);
|
|
19
|
+
config.prompts = config.prompts.filter(scoped);
|
|
20
|
+
});
|
|
21
|
+
nitroApp.hooks.hook("mcp:tool:called", ({ name, result, durationMs, event }) => {
|
|
22
|
+
const principal = event.context.auth;
|
|
23
|
+
console.info("[mcp] tool called", JSON.stringify({
|
|
24
|
+
tool: name,
|
|
25
|
+
userId: principal?.user.id ?? null,
|
|
26
|
+
credential: principal?.kind ?? null,
|
|
27
|
+
outcome: result.isError ? "error" : "success",
|
|
28
|
+
durationMs
|
|
29
|
+
}));
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import type { ZodRawShape, z } from 'zod';
|
|
3
|
+
import type { McpPromptCallbackResult, McpPromptDefinition, McpRequestExtra, McpToolCallbackResult, McpToolDefinition, StandardMcpResourceDefinition } from '@nuxtjs/mcp-toolkit/server';
|
|
4
|
+
import type { AuthPrincipal, AuthUser } from '@frigear-nu/auth/utils';
|
|
5
|
+
import type { ScopedAccess } from '../../utils/access.js';
|
|
6
|
+
type ShapeOutput<Shape extends ZodRawShape> = z.output<z.ZodObject<Shape>>;
|
|
7
|
+
export interface ScopedMcpContext {
|
|
8
|
+
event: H3Event;
|
|
9
|
+
principal: AuthPrincipal;
|
|
10
|
+
/** The authenticated user. "My …" tools must use this and never take a user id as an argument. */
|
|
11
|
+
user: AuthUser;
|
|
12
|
+
/** Whether the credential carries `scope` (e.g. an opt-in `*:pii` scope). */
|
|
13
|
+
hasScope: (scope: string) => boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Extra, module-specific check layered on the scopes, typically the module's `*:authorize` policy
|
|
17
|
+
* hook. It runs on every listing and every call, so a caller the host does not allow never sees
|
|
18
|
+
* the definition. Identify the caller from `principal.user.id`, never from the session on `event`.
|
|
19
|
+
*/
|
|
20
|
+
export type ScopedGate = (event: H3Event, principal: AuthPrincipal) => boolean | Promise<boolean>;
|
|
21
|
+
export interface ScopedOptions extends ScopedAccess {
|
|
22
|
+
gate?: ScopedGate;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Authorises the in-flight request. Listing-time hiding is not authorisation (sessions freeze the
|
|
26
|
+
* list), so every handler re-checks. Failures are 401/403 errors the toolkit turns into `isError`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function authorizeMcp(options: ScopedOptions): Promise<ScopedMcpContext>;
|
|
29
|
+
export type ScopedMcpToolDefinition<Shape extends ZodRawShape = ZodRawShape> = Omit<McpToolDefinition<Shape>, 'handler' | 'enabled' | 'inputSchema'> & ScopedOptions & {
|
|
30
|
+
tags?: string[];
|
|
31
|
+
inputSchema?: Shape;
|
|
32
|
+
handler: (args: ShapeOutput<Shape>, ctx: ScopedMcpContext, extra: McpRequestExtra) => McpToolCallbackResult | Promise<McpToolCallbackResult>;
|
|
33
|
+
};
|
|
34
|
+
/** An MCP tool that is listed and callable only within the scopes (and gate) the caller satisfies. */
|
|
35
|
+
export declare function defineScopedMcpTool<const Shape extends ZodRawShape = Record<string, never>>(definition: ScopedMcpToolDefinition<Shape>): McpToolDefinition<Shape, Readonly<{
|
|
36
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
37
|
+
}>>;
|
|
38
|
+
export type ScopedMcpResourceDefinition = Omit<StandardMcpResourceDefinition, 'handler' | 'enabled'> & ScopedOptions & {
|
|
39
|
+
handler: (ctx: ScopedMcpContext, ...args: Parameters<StandardMcpResourceDefinition['handler']>) => ReturnType<StandardMcpResourceDefinition['handler']>;
|
|
40
|
+
};
|
|
41
|
+
export declare function defineScopedMcpResource(definition: ScopedMcpResourceDefinition): import("@nuxtjs/mcp-toolkit/server").McpResourceDefinition;
|
|
42
|
+
export type ScopedMcpPromptDefinition<Shape extends ZodRawShape | undefined = undefined> = Omit<McpPromptDefinition<Shape>, 'handler' | 'enabled'> & ScopedOptions & {
|
|
43
|
+
handler: (args: Shape extends ZodRawShape ? ShapeOutput<Shape> : Record<string, never>, ctx: ScopedMcpContext) => McpPromptCallbackResult | Promise<McpPromptCallbackResult>;
|
|
44
|
+
};
|
|
45
|
+
export declare function defineScopedMcpPrompt<const Shape extends ZodRawShape | undefined = undefined>(definition: ScopedMcpPromptDefinition<Shape>): McpPromptDefinition<Shape>;
|
|
46
|
+
export { pick, piiNotice, redactPii } from '../../utils/access.js';
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { defineMcpPrompt, defineMcpResource, defineMcpTool } from "@nuxtjs/mcp-toolkit/server";
|
|
2
|
+
import { hasScopes } from "@frigear-nu/auth/utils";
|
|
3
|
+
import { SCOPED_META_KEY, isPrincipalAllowed, scopeTags } from "../../utils/access.js";
|
|
4
|
+
async function isAllowed(event, options) {
|
|
5
|
+
const principal = await getAuth(event);
|
|
6
|
+
if (!isPrincipalAllowed(principal, options)) return null;
|
|
7
|
+
if (!options.gate) return principal;
|
|
8
|
+
try {
|
|
9
|
+
return await options.gate(event, principal) ? principal : null;
|
|
10
|
+
} catch (error) {
|
|
11
|
+
console.error("[mcp] gate failed, hiding definition", error);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function toContext(event, principal) {
|
|
16
|
+
return {
|
|
17
|
+
event,
|
|
18
|
+
principal,
|
|
19
|
+
user: principal.user,
|
|
20
|
+
hasScope: (scope) => hasScopes(principal.scopes, [scope])
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export async function authorizeMcp(options) {
|
|
24
|
+
const event = useEvent();
|
|
25
|
+
const principal = await getAuth(event);
|
|
26
|
+
if (!principal) throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
27
|
+
if (options.sessionOnly && principal.kind !== "session") {
|
|
28
|
+
throw createError({ statusCode: 403, statusMessage: "Forbidden", data: { error: "session_required" } });
|
|
29
|
+
}
|
|
30
|
+
if (!hasScopes(principal.scopes, options.scopes)) {
|
|
31
|
+
throw createError({ statusCode: 403, statusMessage: "Forbidden", data: { error: "insufficient_scope", scopes: options.scopes } });
|
|
32
|
+
}
|
|
33
|
+
if (options.gate && !await options.gate(event, principal)) {
|
|
34
|
+
throw createError({ statusCode: 403, statusMessage: "Forbidden" });
|
|
35
|
+
}
|
|
36
|
+
return toContext(event, principal);
|
|
37
|
+
}
|
|
38
|
+
function assertScoped(kind, options) {
|
|
39
|
+
if (!options.scopes?.length) {
|
|
40
|
+
throw new Error(`[mcp] A ${kind} must declare at least one scope; unscoped definitions are never exposed.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function guarded(options, meta) {
|
|
44
|
+
return {
|
|
45
|
+
tags: scopeTags(options),
|
|
46
|
+
_meta: { ...meta, [SCOPED_META_KEY]: true, "frigear-nu/scopes": options.scopes },
|
|
47
|
+
enabled: async (event) => await isAllowed(event, options) !== null
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function defineScopedMcpTool(definition) {
|
|
51
|
+
assertScoped("tool", definition);
|
|
52
|
+
const { scopes: _scopes, sessionOnly: _sessionOnly, gate: _gate, handler, tags, _meta, inputSchema, ...rest } = definition;
|
|
53
|
+
const base = guarded(definition, _meta);
|
|
54
|
+
return defineMcpTool({
|
|
55
|
+
...rest,
|
|
56
|
+
inputSchema: inputSchema ?? {},
|
|
57
|
+
tags: [...base.tags, ...tags ?? []],
|
|
58
|
+
_meta: base._meta,
|
|
59
|
+
enabled: base.enabled,
|
|
60
|
+
handler: (async (args, extra) => handler(args, await authorizeMcp(definition), extra))
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export function defineScopedMcpResource(definition) {
|
|
64
|
+
assertScoped("resource", definition);
|
|
65
|
+
const { scopes: _scopes, sessionOnly: _sessionOnly, gate: _gate, handler, tags, _meta, ...rest } = definition;
|
|
66
|
+
const base = guarded(definition, _meta);
|
|
67
|
+
return defineMcpResource({
|
|
68
|
+
...rest,
|
|
69
|
+
tags: [...base.tags, ...tags ?? []],
|
|
70
|
+
_meta: base._meta,
|
|
71
|
+
enabled: base.enabled,
|
|
72
|
+
handler: (async (...args) => handler(await authorizeMcp(definition), ...args))
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
export function defineScopedMcpPrompt(definition) {
|
|
76
|
+
assertScoped("prompt", definition);
|
|
77
|
+
const { scopes: _scopes, sessionOnly: _sessionOnly, gate: _gate, handler, tags, _meta, ...rest } = definition;
|
|
78
|
+
const base = guarded(definition, _meta);
|
|
79
|
+
return defineMcpPrompt({
|
|
80
|
+
...rest,
|
|
81
|
+
tags: [...base.tags, ...tags ?? []],
|
|
82
|
+
_meta: base._meta,
|
|
83
|
+
enabled: base.enabled,
|
|
84
|
+
handler: (async (...args) => {
|
|
85
|
+
const ctx = await authorizeMcp(definition);
|
|
86
|
+
return handler(definition.inputSchema ? args[0] : {}, ctx);
|
|
87
|
+
})
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
export { pick, piiNotice, redactPii } from "../../utils/access.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AuthPrincipal } from '@frigear-nu/auth/utils';
|
|
2
|
+
/** Marker set on every definition created through the scoped definers; anything without it is dropped. */
|
|
3
|
+
export declare const SCOPED_META_KEY = "frigear-nu/scoped";
|
|
4
|
+
export interface ScopedAccess {
|
|
5
|
+
/** Scopes the caller's credential must carry. Required: a definition without scopes is never exposed. */
|
|
6
|
+
scopes: string[];
|
|
7
|
+
/** Never exposed to bearer credentials (API tokens, OAuth); only to a cookie session. */
|
|
8
|
+
sessionOnly?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Pure part of the access decision: is there a principal, does it carry the scopes, is the
|
|
12
|
+
* credential kind allowed. Module-specific gates (e.g. `events:authorize`) layer on top.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isPrincipalAllowed(principal: AuthPrincipal | null | undefined, access: ScopedAccess): principal is AuthPrincipal;
|
|
15
|
+
export declare function scopeTags(access: ScopedAccess): string[];
|
|
16
|
+
export interface RedactOptions<T extends object> {
|
|
17
|
+
/** Whether the caller may see identifying fields. */
|
|
18
|
+
allow: boolean;
|
|
19
|
+
/** Fields that identify a person or hold their answers. */
|
|
20
|
+
fields: ReadonlyArray<keyof T>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Removes identifying fields unless explicitly allowed. Use together with an explicit allow-list of
|
|
24
|
+
* output fields: never pass a raw database row through.
|
|
25
|
+
*/
|
|
26
|
+
export declare function redactPii<T extends object>(row: T, options: RedactOptions<T>): Partial<T>;
|
|
27
|
+
/** Picks an explicit allow-list of keys, so a new database column is never exposed by accident. */
|
|
28
|
+
export declare function pick<T extends object, K extends keyof T>(row: T, keys: readonly K[]): Pick<T, K>;
|
|
29
|
+
/**
|
|
30
|
+
* Message for the agent when identifying fields were withheld, so it can tell the user why instead
|
|
31
|
+
* of guessing or retrying. Undefined when nothing was withheld.
|
|
32
|
+
*/
|
|
33
|
+
export declare function piiNotice(allowed: boolean, scope: string, fields: string): string | undefined;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { hasScopes } from "@frigear-nu/auth/utils";
|
|
2
|
+
export const SCOPED_META_KEY = "frigear-nu/scoped";
|
|
3
|
+
export function isPrincipalAllowed(principal, access) {
|
|
4
|
+
if (!principal) return false;
|
|
5
|
+
if (!access.scopes.length) return false;
|
|
6
|
+
if (access.sessionOnly && principal.kind !== "session") return false;
|
|
7
|
+
return hasScopes(principal.scopes, access.scopes);
|
|
8
|
+
}
|
|
9
|
+
export function scopeTags(access) {
|
|
10
|
+
return [...access.scopes.map((scope) => `scope:${scope}`), ...access.sessionOnly ? ["session-only"] : []];
|
|
11
|
+
}
|
|
12
|
+
export function redactPii(row, options) {
|
|
13
|
+
if (options.allow) return { ...row };
|
|
14
|
+
const hidden = new Set(options.fields);
|
|
15
|
+
return Object.fromEntries(Object.entries(row).filter(([key]) => !hidden.has(key)));
|
|
16
|
+
}
|
|
17
|
+
export function pick(row, keys) {
|
|
18
|
+
const result = {};
|
|
19
|
+
for (const key of keys) result[key] = row[key];
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
export function piiNotice(allowed, scope, fields) {
|
|
23
|
+
if (allowed) return void 0;
|
|
24
|
+
return `${fields} are withheld because this credential lacks the "${scope}" scope. This is intentional privacy protection; do not look for another way to obtain them. If the user needs them, ask them to create an API token that also has the "${scope}" scope.`;
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Nuxt } from '@nuxt/schema';
|
|
2
|
+
export type McpDefinitionKind = 'tools' | 'resources' | 'prompts';
|
|
3
|
+
interface McpDefinitionPaths {
|
|
4
|
+
tools: string[];
|
|
5
|
+
resources: string[];
|
|
6
|
+
prompts: string[];
|
|
7
|
+
handlers: string[];
|
|
8
|
+
}
|
|
9
|
+
declare module '@nuxt/schema' {
|
|
10
|
+
interface NuxtHooks {
|
|
11
|
+
'mcp:definitions:paths': (paths: McpDefinitionPaths) => void | Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Register a module's MCP definitions. `dir` is an absolute directory laid out like a project's
|
|
16
|
+
* `server/mcp`: `tools/`, `resources/` and `prompts/` subfolders (each optional). Subfolders of
|
|
17
|
+
* `tools/` become tool groups, so `tools/admin/…` is grouped as `admin`.
|
|
18
|
+
*
|
|
19
|
+
* Every definition must be created with `defineScopedMcpTool` / `defineScopedMcpResource` /
|
|
20
|
+
* `defineScopedMcpPrompt` so it carries the scopes it requires; anything else is dropped at runtime.
|
|
21
|
+
*/
|
|
22
|
+
export declare function addMcpDefinitions(nuxt: Nuxt, dir: string): void;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export function addMcpDefinitions(nuxt, dir) {
|
|
4
|
+
nuxt.hook("mcp:definitions:paths", (paths) => {
|
|
5
|
+
for (const kind of ["tools", "resources", "prompts"]) {
|
|
6
|
+
const path = join(dir, kind);
|
|
7
|
+
if (existsSync(path) && !paths[kind].includes(path)) paths[kind].push(path);
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { addMcpDefinitions } from './build.js';
|
|
2
|
+
export type { McpDefinitionKind } from './build.js';
|
|
3
|
+
export { SCOPED_META_KEY, isPrincipalAllowed, pick, piiNotice, redactPii, scopeTags } from './access.js';
|
|
4
|
+
export type { RedactOptions, ScopedAccess } from './access.js';
|
package/dist/types.d.mts
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,78 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frigear-nu/mcp",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Scoped, PII-safe MCP tools for the frigear ecosystem: modules contribute tools, resources and prompts that are only listed and callable within the scopes the caller holds.",
|
|
5
|
+
"homepage": "https://docs.frigear.dev/modules/mcp",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/frigear-nu/frigear-nu.git",
|
|
9
|
+
"directory": "packages/mcp"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/module.mjs",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/types.d.mts",
|
|
17
|
+
"import": "./dist/module.mjs",
|
|
18
|
+
"default": "./dist/module.mjs"
|
|
19
|
+
},
|
|
20
|
+
"./utils": {
|
|
21
|
+
"types": "./dist/runtime/utils/index.d.ts",
|
|
22
|
+
"import": "./dist/runtime/utils/index.js",
|
|
23
|
+
"default": "./dist/runtime/utils/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"workspaces": [
|
|
30
|
+
"playground"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@frigear-nu/auth": "0.0.2",
|
|
34
|
+
"@frigear-nu/module-kit": "0.1.6",
|
|
35
|
+
"@nuxt/kit": "^4.6.0",
|
|
36
|
+
"@nuxtjs/mcp-toolkit": "^0.24.1",
|
|
37
|
+
"agents": "^0.28.0",
|
|
38
|
+
"h3": "^1.15.11",
|
|
39
|
+
"zod": "^4.6.5"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@libsql/client": "^0.18.0",
|
|
43
|
+
"@nuxt/devtools": "^3.4.2",
|
|
44
|
+
"@nuxt/eslint-config": "^1.17.0",
|
|
45
|
+
"@nuxt/module-builder": "^1.0.3",
|
|
46
|
+
"@nuxt/schema": "^4.6.0",
|
|
47
|
+
"@nuxt/test-utils": "^4.3.2",
|
|
48
|
+
"@nuxthub/core": "^0.10.8",
|
|
49
|
+
"@types/node": "latest",
|
|
50
|
+
"drizzle-kit": "^0.31.11",
|
|
51
|
+
"drizzle-orm": "^0.45.3",
|
|
52
|
+
"eslint": "^10.10.0",
|
|
53
|
+
"nuxt": "^4.6.1",
|
|
54
|
+
"nuxt-auth-utils": "^0.5.30",
|
|
55
|
+
"typescript": "^6.0.3",
|
|
56
|
+
"vitest": "^5.0.1",
|
|
57
|
+
"vue-tsc": "^3.3.11"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"postinstall": "nuxt-module-build build --stub && nuxt prepare",
|
|
61
|
+
"build": "nuxt-module-build build",
|
|
62
|
+
"dev": "npm run dev:prepare && nuxt dev playground",
|
|
63
|
+
"dev:build": "nuxt build playground",
|
|
64
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
|
|
65
|
+
"lint": "eslint .",
|
|
66
|
+
"lint:fix": "eslint . --fix",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"test:watch": "vitest watch",
|
|
69
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
70
|
+
},
|
|
71
|
+
"typesVersions": {
|
|
72
|
+
"*": {
|
|
73
|
+
".": [
|
|
74
|
+
"./dist/types.d.mts"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
6
78
|
}
|
package/README.md
DELETED