@pikku/inspector 0.11.1 → 0.12.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/CHANGELOG.md +26 -1
- package/OPTIMIZATION-PLAN.md +195 -0
- package/dist/add/add-ai-agent.d.ts +2 -0
- package/dist/add/add-ai-agent.js +314 -0
- package/dist/add/add-channel.js +69 -61
- package/dist/add/add-cli.js +36 -18
- package/dist/add/add-file-with-factory.js +2 -0
- package/dist/add/add-functions.js +327 -59
- package/dist/add/add-http-route.d.ts +19 -10
- package/dist/add/add-http-route.js +153 -44
- package/dist/add/add-http-routes.d.ts +5 -0
- package/dist/add/add-http-routes.js +159 -0
- package/dist/add/add-keyed-wiring.d.ts +12 -0
- package/dist/add/add-keyed-wiring.js +97 -0
- package/dist/add/add-mcp-prompt.js +14 -9
- package/dist/add/add-mcp-resource.js +14 -9
- package/dist/add/add-middleware.d.ts +1 -4
- package/dist/add/add-middleware.js +364 -79
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +152 -40
- package/dist/add/add-queue-worker.js +18 -12
- package/dist/add/add-rpc-invocations.d.ts +3 -0
- package/dist/add/add-rpc-invocations.js +65 -25
- package/dist/add/add-schedule.js +11 -5
- package/dist/add/add-secret.d.ts +3 -0
- package/dist/add/add-secret.js +82 -0
- package/dist/add/add-trigger.d.ts +2 -0
- package/dist/add/add-trigger.js +87 -0
- package/dist/add/add-variable.d.ts +1 -0
- package/dist/add/add-variable.js +8 -0
- package/dist/add/add-workflow-graph.d.ts +7 -0
- package/dist/add/add-workflow-graph.js +396 -0
- package/dist/add/add-workflow.js +124 -26
- package/dist/error-codes.d.ts +16 -1
- package/dist/error-codes.js +21 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.js +5 -2
- package/dist/inspector.d.ts +1 -1
- package/dist/inspector.js +106 -13
- package/dist/schema-generator.d.ts +1 -0
- package/dist/schema-generator.js +1 -0
- package/dist/types-map.js +10 -1
- package/dist/types.d.ts +180 -30
- package/dist/utils/compute-required-schemas.d.ts +4 -0
- package/dist/utils/compute-required-schemas.js +41 -0
- package/dist/utils/contract-hashes.d.ts +35 -0
- package/dist/utils/contract-hashes.js +202 -0
- package/dist/utils/custom-types-generator.d.ts +9 -0
- package/dist/utils/custom-types-generator.js +71 -0
- package/dist/utils/detect-schema-vendor.d.ts +22 -0
- package/dist/utils/detect-schema-vendor.js +76 -0
- package/dist/utils/ensure-function-metadata.d.ts +5 -2
- package/dist/utils/ensure-function-metadata.js +220 -6
- package/dist/utils/extract-function-name.d.ts +5 -16
- package/dist/utils/extract-function-name.js +93 -298
- package/dist/utils/extract-services.d.ts +2 -1
- package/dist/utils/extract-services.js +25 -1
- package/dist/utils/filter-inspector-state.js +107 -23
- package/dist/utils/get-property-value.d.ts +8 -2
- package/dist/utils/get-property-value.js +33 -4
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/utils/middleware.d.ts +7 -30
- package/dist/utils/middleware.js +80 -66
- package/dist/utils/permissions.d.ts +2 -2
- package/dist/utils/permissions.js +10 -10
- package/dist/utils/post-process.d.ts +9 -10
- package/dist/utils/post-process.js +231 -24
- package/dist/utils/resolve-external-package.d.ts +12 -0
- package/dist/utils/resolve-external-package.js +34 -0
- package/dist/utils/resolve-function-types.d.ts +6 -0
- package/dist/utils/resolve-function-types.js +29 -0
- package/dist/utils/resolve-identifier.d.ts +10 -0
- package/dist/utils/resolve-identifier.js +36 -0
- package/dist/utils/resolve-versions.d.ts +2 -0
- package/dist/utils/resolve-versions.js +78 -0
- package/dist/utils/schema-generator.d.ts +9 -0
- package/dist/utils/schema-generator.js +209 -0
- package/dist/utils/serialize-inspector-state.d.ts +73 -13
- package/dist/utils/serialize-inspector-state.js +102 -6
- package/dist/utils/serialize-mcp-json.d.ts +2 -0
- package/dist/utils/serialize-mcp-json.js +99 -0
- package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
- package/dist/utils/serialize-middleware-groups-meta.js +28 -0
- package/dist/utils/serialize-openapi-json.d.ts +85 -0
- package/dist/utils/serialize-openapi-json.js +151 -0
- package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
- package/dist/utils/serialize-permissions-groups-meta.js +31 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +830 -0
- package/dist/{workflow/extract-simple-workflow.d.ts → utils/workflow/dsl/extract-dsl-workflow.d.ts} +4 -2
- package/dist/{workflow/extract-simple-workflow.js → utils/workflow/dsl/extract-dsl-workflow.js} +572 -72
- package/dist/utils/workflow/dsl/index.d.ts +7 -0
- package/dist/utils/workflow/dsl/index.js +7 -0
- package/dist/{workflow → utils/workflow/dsl}/patterns.d.ts +21 -0
- package/dist/{workflow → utils/workflow/dsl}/patterns.js +90 -10
- package/dist/{workflow → utils/workflow/dsl}/validation.d.ts +2 -0
- package/dist/{workflow → utils/workflow/dsl}/validation.js +25 -7
- package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +318 -0
- package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
- package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
- package/dist/utils/workflow/graph/index.d.ts +8 -0
- package/dist/utils/workflow/graph/index.js +8 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +35 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +150 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +203 -0
- package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
- package/dist/visit.js +13 -2
- package/package.json +26 -4
- package/src/add/add-ai-agent.ts +468 -0
- package/src/add/add-channel.ts +82 -79
- package/src/add/add-cli.ts +49 -20
- package/src/add/add-file-with-factory.ts +2 -0
- package/src/add/add-functions.ts +429 -71
- package/src/add/add-http-route.ts +246 -65
- package/src/add/add-http-routes.ts +228 -0
- package/src/add/add-keyed-wiring.ts +151 -0
- package/src/add/add-mcp-prompt.ts +26 -15
- package/src/add/add-mcp-resource.ts +27 -15
- package/src/add/add-middleware.ts +482 -80
- package/src/add/add-permission.ts +199 -40
- package/src/add/add-queue-worker.ts +24 -19
- package/src/add/add-rpc-invocations.ts +78 -31
- package/src/add/add-schedule.ts +16 -11
- package/src/add/add-secret.ts +140 -0
- package/src/add/add-trigger.ts +154 -0
- package/src/add/add-variable.ts +9 -0
- package/src/add/add-workflow-graph.ts +522 -0
- package/src/add/add-workflow.ts +117 -30
- package/src/error-codes.ts +26 -1
- package/src/index.ts +27 -8
- package/src/inspector.ts +145 -17
- package/src/schema-generator.ts +1 -0
- package/src/types-map.ts +12 -1
- package/src/types.ts +192 -51
- package/src/utils/compute-required-schemas.ts +49 -0
- package/src/utils/contract-hashes.test.ts +528 -0
- package/src/utils/contract-hashes.ts +290 -0
- package/src/utils/custom-types-generator.ts +88 -0
- package/src/utils/detect-schema-vendor.ts +90 -0
- package/src/utils/ensure-function-metadata.ts +324 -7
- package/src/utils/extract-function-name.ts +108 -358
- package/src/utils/extract-services.ts +35 -2
- package/src/utils/filter-inspector-state.test.ts +34 -20
- package/src/utils/filter-inspector-state.ts +140 -31
- package/src/utils/get-property-value.ts +50 -5
- package/src/utils/hash.ts +26 -0
- package/src/utils/middleware.test.ts +204 -0
- package/src/utils/middleware.ts +129 -67
- package/src/utils/permissions.test.ts +35 -12
- package/src/utils/permissions.ts +10 -10
- package/src/utils/post-process.ts +283 -43
- package/src/utils/resolve-external-package.ts +42 -0
- package/src/utils/resolve-function-types.ts +42 -0
- package/src/utils/resolve-identifier.ts +46 -0
- package/src/utils/resolve-versions.test.ts +249 -0
- package/src/utils/resolve-versions.ts +105 -0
- package/src/utils/schema-generator.ts +329 -0
- package/src/utils/serialize-inspector-state.ts +181 -20
- package/src/utils/serialize-mcp-json.ts +145 -0
- package/src/utils/serialize-middleware-groups-meta.ts +33 -0
- package/src/utils/serialize-openapi-json.ts +277 -0
- package/src/utils/serialize-permissions-groups-meta.ts +35 -0
- package/src/utils/test-data/inspector-state.json +69 -66
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1104 -0
- package/src/{workflow/extract-simple-workflow.ts → utils/workflow/dsl/extract-dsl-workflow.ts} +678 -85
- package/src/utils/workflow/dsl/index.ts +11 -0
- package/src/{workflow → utils/workflow/dsl}/patterns.ts +108 -11
- package/src/{workflow → utils/workflow/dsl}/validation.ts +34 -7
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +422 -0
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
- package/src/utils/workflow/graph/index.ts +11 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +216 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +231 -0
- package/src/visit.ts +14 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add/add-mcp-tool.d.ts +0 -2
- package/dist/add/add-mcp-tool.js +0 -81
- package/dist/utils/extract-service-metadata.d.ts +0 -19
- package/dist/utils/extract-service-metadata.js +0 -244
- package/dist/utils/write-service-metadata.d.ts +0 -13
- package/dist/utils/write-service-metadata.js +0 -37
- package/src/add/add-mcp-tool.ts +0 -141
- package/src/utils/extract-service-metadata.ts +0 -353
- package/src/utils/write-service-metadata.ts +0 -51
package/dist/add/add-mcp-tool.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript';
|
|
2
|
-
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
3
|
-
import { extractWireNames } from '../utils/post-process.js';
|
|
4
|
-
import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js';
|
|
5
|
-
import { extractFunctionName } from '../utils/extract-function-name.js';
|
|
6
|
-
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
|
|
7
|
-
import { resolveMiddleware } from '../utils/middleware.js';
|
|
8
|
-
import { resolvePermissions } from '../utils/permissions.js';
|
|
9
|
-
import { ErrorCode } from '../error-codes.js';
|
|
10
|
-
export const addMCPTool = (logger, node, checker, state, options) => {
|
|
11
|
-
if (!ts.isCallExpression(node)) {
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
const args = node.arguments;
|
|
15
|
-
const firstArg = args[0];
|
|
16
|
-
const expression = node.expression;
|
|
17
|
-
// Check if the call is to wireMCPTool
|
|
18
|
-
if (!ts.isIdentifier(expression) || expression.text !== 'wireMCPTool') {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
if (!firstArg) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
25
|
-
const obj = firstArg;
|
|
26
|
-
const nameValue = getPropertyValue(obj, 'name');
|
|
27
|
-
const titleValue = getPropertyValue(obj, 'title');
|
|
28
|
-
const { tags, summary, description, errors } = getCommonWireMetaData(obj, 'MCP tool', nameValue, logger);
|
|
29
|
-
const streamingValue = getPropertyValue(obj, 'streaming');
|
|
30
|
-
if (streamingValue === true) {
|
|
31
|
-
logger.warn(`MCP tool '${nameValue}' has streaming enabled, but streaming is not yet supported.`);
|
|
32
|
-
}
|
|
33
|
-
const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
|
|
34
|
-
if (!funcInitializer) {
|
|
35
|
-
logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for MCP tool '${nameValue}'.`);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const pikkuFuncName = extractFunctionName(funcInitializer, checker, state.rootDir).pikkuFuncName;
|
|
39
|
-
// Ensure function metadata exists (creates stub for inline functions)
|
|
40
|
-
ensureFunctionMetadata(state, pikkuFuncName, nameValue || undefined);
|
|
41
|
-
if (!nameValue) {
|
|
42
|
-
logger.critical(ErrorCode.MISSING_NAME, "MCP tool is missing the required 'name' property.");
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (!description) {
|
|
46
|
-
logger.critical(ErrorCode.MISSING_DESCRIPTION, `MCP tool '${nameValue}' is missing a description.`);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
// lookup existing function metadata
|
|
50
|
-
const fnMeta = state.functions.meta[pikkuFuncName];
|
|
51
|
-
if (!fnMeta) {
|
|
52
|
-
logger.critical(ErrorCode.FUNCTION_METADATA_NOT_FOUND, `No function metadata found for '${pikkuFuncName}'.`);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
const inputSchema = fnMeta.inputs?.[0] || null;
|
|
56
|
-
const outputSchema = fnMeta.outputs?.[0] || null;
|
|
57
|
-
// --- resolve middleware ---
|
|
58
|
-
const middleware = resolveMiddleware(state, obj, tags, checker);
|
|
59
|
-
// --- resolve permissions ---
|
|
60
|
-
const permissions = resolvePermissions(state, obj, tags, checker);
|
|
61
|
-
// --- track used functions/middleware/permissions for service aggregation ---
|
|
62
|
-
state.serviceAggregation.usedFunctions.add(pikkuFuncName);
|
|
63
|
-
extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
|
|
64
|
-
extractWireNames(permissions).forEach((name) => state.serviceAggregation.usedPermissions.add(name));
|
|
65
|
-
state.mcpEndpoints.files.add(node.getSourceFile().fileName);
|
|
66
|
-
state.mcpEndpoints.toolsMeta[nameValue] = {
|
|
67
|
-
pikkuFuncName,
|
|
68
|
-
name: nameValue,
|
|
69
|
-
title: titleValue || undefined,
|
|
70
|
-
description,
|
|
71
|
-
summary,
|
|
72
|
-
errors,
|
|
73
|
-
...(streamingValue !== null && { streaming: streamingValue }),
|
|
74
|
-
tags,
|
|
75
|
-
inputSchema,
|
|
76
|
-
outputSchema,
|
|
77
|
-
middleware,
|
|
78
|
-
permissions,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript';
|
|
2
|
-
export interface ServiceMetadata {
|
|
3
|
-
name: string;
|
|
4
|
-
summary: string;
|
|
5
|
-
description: string;
|
|
6
|
-
package: string;
|
|
7
|
-
path: string;
|
|
8
|
-
version: string;
|
|
9
|
-
interface: string;
|
|
10
|
-
expandedProperties: Record<string, string>;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Extract metadata for a service from its TypeScript declaration
|
|
14
|
-
*/
|
|
15
|
-
export declare function extractServiceMetadata(serviceName: string, type: ts.Type, checker: ts.TypeChecker, rootDir: string): ServiceMetadata | null;
|
|
16
|
-
/**
|
|
17
|
-
* Extract metadata for all services in a type
|
|
18
|
-
*/
|
|
19
|
-
export declare function extractAllServiceMetadata(servicesType: ts.Type, checker: ts.TypeChecker, rootDir: string): ServiceMetadata[];
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
/**
|
|
5
|
-
* Extract JSDoc comment information from a TypeScript node
|
|
6
|
-
*/
|
|
7
|
-
function extractJSDoc(node) {
|
|
8
|
-
const jsDocTags = ts.getJSDocTags(node);
|
|
9
|
-
const jsDocComments = ts.getJSDocCommentsAndTags(node);
|
|
10
|
-
let summary = '';
|
|
11
|
-
let description = '';
|
|
12
|
-
const summaryTag = jsDocTags.find((tag) => tag.tagName.text === 'summary');
|
|
13
|
-
if (summaryTag && summaryTag.comment) {
|
|
14
|
-
summary =
|
|
15
|
-
typeof summaryTag.comment === 'string'
|
|
16
|
-
? summaryTag.comment
|
|
17
|
-
: summaryTag.comment.map((c) => c.text).join('');
|
|
18
|
-
}
|
|
19
|
-
for (const comment of jsDocComments) {
|
|
20
|
-
if (ts.isJSDoc(comment) && comment.comment) {
|
|
21
|
-
const commentText = typeof comment.comment === 'string'
|
|
22
|
-
? comment.comment
|
|
23
|
-
: comment.comment.map((c) => c.text).join('');
|
|
24
|
-
if (!summary && commentText) {
|
|
25
|
-
const lines = commentText
|
|
26
|
-
.split('\n')
|
|
27
|
-
.map((l) => l.trim())
|
|
28
|
-
.filter(Boolean);
|
|
29
|
-
if (lines.length > 0) {
|
|
30
|
-
summary = lines[0];
|
|
31
|
-
if (lines.length > 1) {
|
|
32
|
-
description = lines.slice(1).join('\n').trim();
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
description = commentText;
|
|
38
|
-
}
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return { summary, description };
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Serialize a TypeScript type to a string representation
|
|
46
|
-
*/
|
|
47
|
-
function serializeTypeToString(node, sourceFile, checker) {
|
|
48
|
-
const nodeSourceFile = node.getSourceFile();
|
|
49
|
-
if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
|
|
50
|
-
return node.getText(nodeSourceFile);
|
|
51
|
-
}
|
|
52
|
-
if (ts.isClassDeclaration(node)) {
|
|
53
|
-
return serializePublicClassMembers(node, nodeSourceFile, checker);
|
|
54
|
-
}
|
|
55
|
-
const type = checker.getTypeAtLocation(node);
|
|
56
|
-
return checker.typeToString(type, node, ts.TypeFormatFlags.NoTruncation);
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Extract public members from a class and serialize them
|
|
60
|
-
*/
|
|
61
|
-
function serializePublicClassMembers(classNode, sourceFile, checker) {
|
|
62
|
-
const className = classNode.name?.text || 'UnnamedClass';
|
|
63
|
-
const publicMembers = [];
|
|
64
|
-
for (const member of classNode.members) {
|
|
65
|
-
const modifiers = ts.canHaveModifiers(member)
|
|
66
|
-
? ts.getModifiers(member)
|
|
67
|
-
: undefined;
|
|
68
|
-
const isPublic = !modifiers?.some((mod) => mod.kind === ts.SyntaxKind.PrivateKeyword ||
|
|
69
|
-
mod.kind === ts.SyntaxKind.ProtectedKeyword);
|
|
70
|
-
if (!isPublic)
|
|
71
|
-
continue;
|
|
72
|
-
if (ts.isMethodDeclaration(member) ||
|
|
73
|
-
ts.isPropertyDeclaration(member) ||
|
|
74
|
-
ts.isConstructorDeclaration(member)) {
|
|
75
|
-
const memberSignature = getMemberSignature(member, sourceFile, checker);
|
|
76
|
-
if (memberSignature) {
|
|
77
|
-
publicMembers.push(memberSignature);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return `class ${className} {\n ${publicMembers.join('\n ')}\n}`;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Extract a clean signature for a class member (without implementation)
|
|
85
|
-
*/
|
|
86
|
-
function getMemberSignature(member, sourceFile, checker) {
|
|
87
|
-
if (ts.isPropertyDeclaration(member)) {
|
|
88
|
-
const name = member.name.getText(sourceFile);
|
|
89
|
-
const type = member.type ? member.type.getText(sourceFile) : 'any';
|
|
90
|
-
const optional = member.questionToken ? '?' : '';
|
|
91
|
-
return `${name}${optional}: ${type};`;
|
|
92
|
-
}
|
|
93
|
-
if (ts.isMethodDeclaration(member)) {
|
|
94
|
-
const name = member.name.getText(sourceFile);
|
|
95
|
-
const typeParams = member.typeParameters
|
|
96
|
-
? `<${member.typeParameters.map((tp) => tp.getText(sourceFile)).join(', ')}>`
|
|
97
|
-
: '';
|
|
98
|
-
const params = member.parameters
|
|
99
|
-
.map((p) => p.getText(sourceFile))
|
|
100
|
-
.join(', ');
|
|
101
|
-
const returnType = member.type ? member.type.getText(sourceFile) : 'void';
|
|
102
|
-
const optional = member.questionToken ? '?' : '';
|
|
103
|
-
return `${name}${optional}${typeParams}(${params}): ${returnType};`;
|
|
104
|
-
}
|
|
105
|
-
if (ts.isConstructorDeclaration(member)) {
|
|
106
|
-
const params = member.parameters
|
|
107
|
-
.map((p) => p.getText(sourceFile))
|
|
108
|
-
.join(', ');
|
|
109
|
-
return `constructor(${params});`;
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Find the nearest package.json and extract package name and version
|
|
115
|
-
*/
|
|
116
|
-
function getPackageInfo(filePath) {
|
|
117
|
-
let currentDir = path.dirname(filePath);
|
|
118
|
-
const root = path.parse(currentDir).root;
|
|
119
|
-
while (currentDir !== root) {
|
|
120
|
-
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
121
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
122
|
-
try {
|
|
123
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
124
|
-
return {
|
|
125
|
-
packageName: packageJson.name || 'unknown',
|
|
126
|
-
version: packageJson.version || '0.0.0',
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
catch (err) {
|
|
130
|
-
// If we can't parse the package.json, continue searching
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
currentDir = path.dirname(currentDir);
|
|
134
|
-
}
|
|
135
|
-
return { packageName: 'unknown', version: '0.0.0' };
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Expand a type to show all its properties including inherited ones
|
|
139
|
-
* Returns a Record mapping property names to their type strings
|
|
140
|
-
*/
|
|
141
|
-
function expandInterfaceProperties(type, checker, maxDepth = 2, currentDepth = 0, visited = new Set()) {
|
|
142
|
-
const result = {};
|
|
143
|
-
if (visited.has(type) || currentDepth >= maxDepth) {
|
|
144
|
-
return result;
|
|
145
|
-
}
|
|
146
|
-
visited.add(type);
|
|
147
|
-
const properties = type.getProperties();
|
|
148
|
-
for (const prop of properties) {
|
|
149
|
-
const propName = prop.getName();
|
|
150
|
-
const propDecl = prop.valueDeclaration || prop.declarations?.[0];
|
|
151
|
-
if (!propDecl)
|
|
152
|
-
continue;
|
|
153
|
-
try {
|
|
154
|
-
const propType = checker.getTypeOfSymbolAtLocation(prop, propDecl);
|
|
155
|
-
const isOptional = !!(prop.flags & ts.SymbolFlags.Optional);
|
|
156
|
-
let typeString = checker.typeToString(propType, propDecl, ts.TypeFormatFlags.NoTruncation |
|
|
157
|
-
ts.TypeFormatFlags.UseFullyQualifiedType);
|
|
158
|
-
if (isOptional && !typeString.includes('undefined')) {
|
|
159
|
-
typeString = `${typeString} | undefined`;
|
|
160
|
-
}
|
|
161
|
-
result[propName] = typeString;
|
|
162
|
-
}
|
|
163
|
-
catch (err) {
|
|
164
|
-
result[propName] = 'any';
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return result;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Extract metadata for a service from its TypeScript declaration
|
|
171
|
-
*/
|
|
172
|
-
export function extractServiceMetadata(serviceName, type, checker, rootDir) {
|
|
173
|
-
const property = type.getProperty(serviceName);
|
|
174
|
-
if (!property) {
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
const declaration = property.valueDeclaration || property.declarations?.[0];
|
|
178
|
-
if (!declaration) {
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
const sourceFile = declaration.getSourceFile();
|
|
182
|
-
const filePath = sourceFile.fileName;
|
|
183
|
-
const serviceType = checker.getTypeOfSymbolAtLocation(property, declaration);
|
|
184
|
-
let typeDeclaration = null;
|
|
185
|
-
if (serviceType.symbol) {
|
|
186
|
-
const typeDecl = serviceType.symbol.valueDeclaration ||
|
|
187
|
-
serviceType.symbol.declarations?.[0];
|
|
188
|
-
if (typeDecl &&
|
|
189
|
-
(ts.isInterfaceDeclaration(typeDecl) ||
|
|
190
|
-
ts.isClassDeclaration(typeDecl) ||
|
|
191
|
-
ts.isTypeAliasDeclaration(typeDecl))) {
|
|
192
|
-
typeDeclaration = typeDecl;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
let summary = '';
|
|
196
|
-
let description = '';
|
|
197
|
-
if (typeDeclaration) {
|
|
198
|
-
const jsDoc = extractJSDoc(typeDeclaration);
|
|
199
|
-
summary = jsDoc.summary;
|
|
200
|
-
description = jsDoc.description;
|
|
201
|
-
}
|
|
202
|
-
else if (ts.isPropertySignature(declaration) ||
|
|
203
|
-
ts.isPropertyDeclaration(declaration)) {
|
|
204
|
-
const jsDoc = extractJSDoc(declaration);
|
|
205
|
-
summary = jsDoc.summary;
|
|
206
|
-
description = jsDoc.description;
|
|
207
|
-
}
|
|
208
|
-
let interfaceString = '';
|
|
209
|
-
if (typeDeclaration) {
|
|
210
|
-
interfaceString = serializeTypeToString(typeDeclaration, sourceFile, checker);
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
interfaceString = checker.typeToString(serviceType, declaration, ts.TypeFormatFlags.NoTruncation);
|
|
214
|
-
}
|
|
215
|
-
const { packageName, version } = getPackageInfo(filePath);
|
|
216
|
-
const relativePath = path.relative(rootDir, filePath);
|
|
217
|
-
const expandedProperties = expandInterfaceProperties(serviceType, checker);
|
|
218
|
-
return {
|
|
219
|
-
name: serviceName,
|
|
220
|
-
summary,
|
|
221
|
-
description,
|
|
222
|
-
package: packageName,
|
|
223
|
-
path: relativePath,
|
|
224
|
-
version,
|
|
225
|
-
interface: interfaceString,
|
|
226
|
-
expandedProperties,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Extract metadata for all services in a type
|
|
231
|
-
*/
|
|
232
|
-
export function extractAllServiceMetadata(servicesType, checker, rootDir) {
|
|
233
|
-
const metadata = [];
|
|
234
|
-
const serviceNames = servicesType
|
|
235
|
-
.getProperties()
|
|
236
|
-
.map((prop) => prop.getName());
|
|
237
|
-
for (const serviceName of serviceNames) {
|
|
238
|
-
const serviceMeta = extractServiceMetadata(serviceName, servicesType, checker, rootDir);
|
|
239
|
-
if (serviceMeta) {
|
|
240
|
-
metadata.push(serviceMeta);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return metadata;
|
|
244
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { ServiceMetadata } from './extract-service-metadata.js';
|
|
2
|
-
/**
|
|
3
|
-
* Write service metadata to a JSON file in .pikku/services directory
|
|
4
|
-
*/
|
|
5
|
-
export declare function writeServiceMetadata(serviceMeta: ServiceMetadata, outDir: string): void;
|
|
6
|
-
/**
|
|
7
|
-
* Write all service metadata files
|
|
8
|
-
*/
|
|
9
|
-
export declare function writeAllServiceMetadata(servicesMetadata: ServiceMetadata[], outDir: string): void;
|
|
10
|
-
/**
|
|
11
|
-
* Clean up services directory (remove old service JSON files)
|
|
12
|
-
*/
|
|
13
|
-
export declare function cleanServicesDirectory(outDir: string): void;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
/**
|
|
4
|
-
* Write service metadata to a JSON file in .pikku/services directory
|
|
5
|
-
*/
|
|
6
|
-
export function writeServiceMetadata(serviceMeta, outDir) {
|
|
7
|
-
const servicesDir = path.join(outDir, 'services');
|
|
8
|
-
if (!fs.existsSync(servicesDir)) {
|
|
9
|
-
fs.mkdirSync(servicesDir, { recursive: true });
|
|
10
|
-
}
|
|
11
|
-
const fileName = `${serviceMeta.name}.gen.json`;
|
|
12
|
-
const filePath = path.join(servicesDir, fileName);
|
|
13
|
-
const jsonContent = JSON.stringify(serviceMeta, null, 2);
|
|
14
|
-
fs.writeFileSync(filePath, jsonContent, 'utf-8');
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Write all service metadata files
|
|
18
|
-
*/
|
|
19
|
-
export function writeAllServiceMetadata(servicesMetadata, outDir) {
|
|
20
|
-
for (const serviceMeta of servicesMetadata) {
|
|
21
|
-
writeServiceMetadata(serviceMeta, outDir);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Clean up services directory (remove old service JSON files)
|
|
26
|
-
*/
|
|
27
|
-
export function cleanServicesDirectory(outDir) {
|
|
28
|
-
const servicesDir = path.join(outDir, 'services');
|
|
29
|
-
if (fs.existsSync(servicesDir)) {
|
|
30
|
-
const files = fs.readdirSync(servicesDir);
|
|
31
|
-
for (const file of files) {
|
|
32
|
-
if (file.endsWith('.gen.json')) {
|
|
33
|
-
fs.unlinkSync(path.join(servicesDir, file));
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
package/src/add/add-mcp-tool.ts
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
import {
|
|
3
|
-
getPropertyValue,
|
|
4
|
-
getCommonWireMetaData,
|
|
5
|
-
} from '../utils/get-property-value.js'
|
|
6
|
-
import { extractWireNames } from '../utils/post-process.js'
|
|
7
|
-
import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js'
|
|
8
|
-
import { AddWiring } from '../types.js'
|
|
9
|
-
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
10
|
-
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
11
|
-
import { resolveMiddleware } from '../utils/middleware.js'
|
|
12
|
-
import { resolvePermissions } from '../utils/permissions.js'
|
|
13
|
-
import { ErrorCode } from '../error-codes.js'
|
|
14
|
-
|
|
15
|
-
export const addMCPTool: AddWiring = (
|
|
16
|
-
logger,
|
|
17
|
-
node,
|
|
18
|
-
checker,
|
|
19
|
-
state,
|
|
20
|
-
options
|
|
21
|
-
) => {
|
|
22
|
-
if (!ts.isCallExpression(node)) {
|
|
23
|
-
return
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const args = node.arguments
|
|
27
|
-
const firstArg = args[0]
|
|
28
|
-
const expression = node.expression
|
|
29
|
-
|
|
30
|
-
// Check if the call is to wireMCPTool
|
|
31
|
-
if (!ts.isIdentifier(expression) || expression.text !== 'wireMCPTool') {
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (!firstArg) {
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
40
|
-
const obj = firstArg
|
|
41
|
-
|
|
42
|
-
const nameValue = getPropertyValue(obj, 'name') as string | null
|
|
43
|
-
const titleValue = getPropertyValue(obj, 'title') as string | null
|
|
44
|
-
const { tags, summary, description, errors } = getCommonWireMetaData(
|
|
45
|
-
obj,
|
|
46
|
-
'MCP tool',
|
|
47
|
-
nameValue,
|
|
48
|
-
logger
|
|
49
|
-
)
|
|
50
|
-
const streamingValue = getPropertyValue(obj, 'streaming') as boolean | null
|
|
51
|
-
|
|
52
|
-
if (streamingValue === true) {
|
|
53
|
-
logger.warn(
|
|
54
|
-
`MCP tool '${nameValue}' has streaming enabled, but streaming is not yet supported.`
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const funcInitializer = getPropertyAssignmentInitializer(
|
|
59
|
-
obj,
|
|
60
|
-
'func',
|
|
61
|
-
true,
|
|
62
|
-
checker
|
|
63
|
-
)
|
|
64
|
-
if (!funcInitializer) {
|
|
65
|
-
logger.critical(
|
|
66
|
-
ErrorCode.MISSING_FUNC,
|
|
67
|
-
`No valid 'func' property for MCP tool '${nameValue}'.`
|
|
68
|
-
)
|
|
69
|
-
return
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const pikkuFuncName = extractFunctionName(
|
|
73
|
-
funcInitializer,
|
|
74
|
-
checker,
|
|
75
|
-
state.rootDir
|
|
76
|
-
).pikkuFuncName
|
|
77
|
-
|
|
78
|
-
// Ensure function metadata exists (creates stub for inline functions)
|
|
79
|
-
ensureFunctionMetadata(state, pikkuFuncName, nameValue || undefined)
|
|
80
|
-
|
|
81
|
-
if (!nameValue) {
|
|
82
|
-
logger.critical(
|
|
83
|
-
ErrorCode.MISSING_NAME,
|
|
84
|
-
"MCP tool is missing the required 'name' property."
|
|
85
|
-
)
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (!description) {
|
|
90
|
-
logger.critical(
|
|
91
|
-
ErrorCode.MISSING_DESCRIPTION,
|
|
92
|
-
`MCP tool '${nameValue}' is missing a description.`
|
|
93
|
-
)
|
|
94
|
-
return
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// lookup existing function metadata
|
|
98
|
-
const fnMeta = state.functions.meta[pikkuFuncName]
|
|
99
|
-
if (!fnMeta) {
|
|
100
|
-
logger.critical(
|
|
101
|
-
ErrorCode.FUNCTION_METADATA_NOT_FOUND,
|
|
102
|
-
`No function metadata found for '${pikkuFuncName}'.`
|
|
103
|
-
)
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
const inputSchema = fnMeta.inputs?.[0] || null
|
|
107
|
-
const outputSchema = fnMeta.outputs?.[0] || null
|
|
108
|
-
|
|
109
|
-
// --- resolve middleware ---
|
|
110
|
-
const middleware = resolveMiddleware(state, obj, tags, checker)
|
|
111
|
-
|
|
112
|
-
// --- resolve permissions ---
|
|
113
|
-
const permissions = resolvePermissions(state, obj, tags, checker)
|
|
114
|
-
|
|
115
|
-
// --- track used functions/middleware/permissions for service aggregation ---
|
|
116
|
-
state.serviceAggregation.usedFunctions.add(pikkuFuncName)
|
|
117
|
-
extractWireNames(middleware).forEach((name) =>
|
|
118
|
-
state.serviceAggregation.usedMiddleware.add(name)
|
|
119
|
-
)
|
|
120
|
-
extractWireNames(permissions).forEach((name) =>
|
|
121
|
-
state.serviceAggregation.usedPermissions.add(name)
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
state.mcpEndpoints.files.add(node.getSourceFile().fileName)
|
|
125
|
-
|
|
126
|
-
state.mcpEndpoints.toolsMeta[nameValue] = {
|
|
127
|
-
pikkuFuncName,
|
|
128
|
-
name: nameValue,
|
|
129
|
-
title: titleValue || undefined,
|
|
130
|
-
description,
|
|
131
|
-
summary,
|
|
132
|
-
errors,
|
|
133
|
-
...(streamingValue !== null && { streaming: streamingValue }),
|
|
134
|
-
tags,
|
|
135
|
-
inputSchema,
|
|
136
|
-
outputSchema,
|
|
137
|
-
middleware,
|
|
138
|
-
permissions,
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|