@appport/mcp 1.0.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/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # @appport/mcp
2
+
3
+ Expose AppPort capabilities as MCP tools.
4
+
5
+ ```ts
6
+ import { createMcpBridge } from "@appport/mcp";
7
+
8
+ const bridge = createMcpBridge(application, { session });
9
+
10
+ bridge.listTools();
11
+ await bridge.callTool("documents_save", { documentId, content });
12
+ ```
13
+
14
+ The layering matters:
15
+
16
+ ```
17
+ AI -> MCP -> AppPort -> application
18
+ ```
19
+
20
+ AppPort does not compete with MCP; it is the application protocol underneath it.
21
+ A capability that is already defined, validated, versioned and authorized becomes
22
+ an agent tool without a second definition, and the agent's session decides what
23
+ it may call. A tool call runs the same dispatch pipeline as any other request, so
24
+ `INVALID_INPUT` and `FORBIDDEN` reach the agent as tool errors rather than as
25
+ unexpected behaviour.
26
+
27
+ Tool names map to capability names (`documents.save` becomes `documents_save`)
28
+ and the tool's input schema is the capability's published JSON Schema. Stream
29
+ capabilities and the reserved `appport.*` namespace are excluded by default.
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `@appport/mcp` — expose AppPort capabilities as MCP tools (§65).
3
+ *
4
+ * The layering matters: AI → MCP → AppPort → application. AppPort does not
5
+ * compete with MCP; it is the application protocol underneath it, so a
6
+ * capability that is already defined, validated and authorized becomes an agent
7
+ * tool without a second definition.
8
+ */
9
+ import { type AppPortManifest, type CapabilityManifestEntry, type JSONSchema } from "@appport/protocol";
10
+ import type { AppPortApplication, DispatchOptions } from "@appport/core";
11
+ /** The subset of the MCP tool descriptor AppPort can fill in from a manifest. */
12
+ export interface McpTool {
13
+ name: string;
14
+ description: string;
15
+ inputSchema: JSONSchema;
16
+ /** Non-standard annotation naming the capability this tool came from. */
17
+ _appport: {
18
+ capability: string;
19
+ version: number;
20
+ authorization: string[];
21
+ outputSchema: JSONSchema;
22
+ };
23
+ }
24
+ export interface McpContent {
25
+ type: "text";
26
+ text: string;
27
+ }
28
+ export interface McpToolResult {
29
+ content: McpContent[];
30
+ isError?: boolean;
31
+ }
32
+ export interface McpBridgeOptions {
33
+ /** Capability names to expose. Defaults to every request capability. */
34
+ include?: (entry: CapabilityManifestEntry) => boolean;
35
+ /** Prefix applied to generated tool names, e.g. `docs`. */
36
+ prefix?: string;
37
+ /** Expose the reserved `appport.*` capabilities. Defaults to false. */
38
+ includeBuiltins?: boolean;
39
+ }
40
+ /** MCP tool names are conventionally `[a-z0-9_-]`; capability names are dotted. */
41
+ export declare function toToolName(capability: string, prefix?: string): string;
42
+ export declare function fromToolName(tool: string, prefix?: string): string;
43
+ /** Renders the manifest's request capabilities as MCP tool descriptors. */
44
+ export declare function toMcpTools(manifest: AppPortManifest, options?: McpBridgeOptions): McpTool[];
45
+ /**
46
+ * A minimal MCP server surface over an AppPort application.
47
+ *
48
+ * Wire `listTools` and `callTool` into an MCP server implementation; the
49
+ * dispatch pipeline — validation, authorization, telemetry — is unchanged, so
50
+ * an agent has exactly the access its session grants and no more.
51
+ */
52
+ export interface McpBridge {
53
+ listTools(): McpTool[];
54
+ callTool(name: string, args: unknown): Promise<McpToolResult>;
55
+ }
56
+ export declare function createMcpBridge(application: AppPortApplication, dispatch?: DispatchOptions, options?: McpBridgeOptions): McpBridge;
57
+ /** Turns an AppPort error into the text an agent should see. */
58
+ export declare function describeError(error: unknown): string;
59
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEzE,iFAAiF;AACjF,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB,yEAAyE;IACzE,QAAQ,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,EAAE,UAAU,CAAA;KAAE,CAAC;CACtG;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC;IACtD,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAGtE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlE;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,EAAE,CAmB/F;AAED;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,SAAS,IAAI,OAAO,EAAE,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAC/D;AAED,wBAAgB,eAAe,CAC7B,WAAW,EAAE,kBAAkB,EAC/B,QAAQ,GAAE,eAAoB,EAC9B,OAAO,GAAE,gBAAqB,GAC7B,SAAS,CAiCX;AAED,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD"}
package/dist/index.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * `@appport/mcp` — expose AppPort capabilities as MCP tools (§65).
3
+ *
4
+ * The layering matters: AI → MCP → AppPort → application. AppPort does not
5
+ * compete with MCP; it is the application protocol underneath it, so a
6
+ * capability that is already defined, validated and authorized becomes an agent
7
+ * tool without a second definition.
8
+ */
9
+ import { AppPortError, createRequest, newRequestId } from "@appport/protocol";
10
+ /** MCP tool names are conventionally `[a-z0-9_-]`; capability names are dotted. */
11
+ export function toToolName(capability, prefix) {
12
+ const base = capability.replace(/\./g, "_");
13
+ return (prefix ? `${prefix}_${base}` : base).slice(0, 64);
14
+ }
15
+ export function fromToolName(tool, prefix) {
16
+ const withoutPrefix = prefix && tool.startsWith(`${prefix}_`) ? tool.slice(prefix.length + 1) : tool;
17
+ return withoutPrefix.replace(/_/g, ".");
18
+ }
19
+ /** Renders the manifest's request capabilities as MCP tool descriptors. */
20
+ export function toMcpTools(manifest, options = {}) {
21
+ const includeBuiltins = options.includeBuiltins ?? false;
22
+ return manifest.capabilities
23
+ .filter((entry) => entry.kind === "request")
24
+ .filter((entry) => includeBuiltins || !entry.name.startsWith("appport."))
25
+ .filter((entry) => options.include?.(entry) ?? true)
26
+ .map((entry) => ({
27
+ name: toToolName(entry.name, options.prefix),
28
+ description: entry.description ??
29
+ `Invokes the ${entry.name} capability of ${manifest.application.name} (version ${entry.latestVersion}).`,
30
+ inputSchema: entry.inputSchema,
31
+ _appport: {
32
+ capability: entry.name,
33
+ version: entry.latestVersion,
34
+ authorization: entry.authorization,
35
+ outputSchema: entry.outputSchema
36
+ }
37
+ }));
38
+ }
39
+ export function createMcpBridge(application, dispatch = {}, options = {}) {
40
+ const tools = () => toMcpTools(application.manifest(), options);
41
+ return {
42
+ listTools: tools,
43
+ async callTool(name, args) {
44
+ const tool = tools().find((entry) => entry.name === name);
45
+ if (!tool) {
46
+ return {
47
+ isError: true,
48
+ content: [{ type: "text", text: `Unknown tool: ${name}` }]
49
+ };
50
+ }
51
+ const response = await application.handleRequest(createRequest({
52
+ requestId: newRequestId(),
53
+ capability: { name: tool._appport.capability, version: tool._appport.version },
54
+ input: args ?? {}
55
+ }), { transport: "mcp", ...dispatch });
56
+ if (!response.ok) {
57
+ return {
58
+ isError: true,
59
+ content: [{ type: "text", text: `${response.error.code}: ${response.error.message}` }]
60
+ };
61
+ }
62
+ return { content: [{ type: "text", text: JSON.stringify(response.output, null, 2) }] };
63
+ }
64
+ };
65
+ }
66
+ /** Turns an AppPort error into the text an agent should see. */
67
+ export function describeError(error) {
68
+ return AppPortError.is(error) ? `${error.code}: ${error.message}` : "INTERNAL_ERROR: the capability failed";
69
+ }
70
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@appport/mcp",
3
+ "version": "1.0.0",
4
+ "description": "Expose AppPort capabilities as MCP tools.",
5
+ "license": "Apache-2.0",
6
+ "repository": { "type": "git", "url": "git+https://github.com/rkendel1/appport.git", "directory": "packages/mcp" },
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc -b",
22
+ "clean": "tsc -b --clean"
23
+ },
24
+ "dependencies": {
25
+ "@appport/protocol": "1.0.0",
26
+ "@appport/core": "1.0.1"
27
+ },
28
+ "publishConfig": { "access": "public" }
29
+ }