@ampless/mcp-server 0.2.0-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ampless contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # @ampless/mcp-server
2
+
3
+ MCP (Model Context Protocol) server for [ampless](https://github.com/heavymoons/ampless). Lets AI agents — Claude Desktop, Cursor, Claude Code, and anything else that speaks MCP — read and write posts and upload media on your CMS instance.
4
+
5
+ > **Pre-release / alpha.** Breaking changes possible in any minor version until v1.0.
6
+
7
+ ## Tools
8
+
9
+ | Tool | What it does |
10
+ |---|---|
11
+ | `list_posts` | List posts with optional `status` filter and pagination |
12
+ | `get_post` | Fetch a single post by `slug` or `postId` |
13
+ | `create_post` | Create a new post (draft or published) |
14
+ | `update_post` | Patch fields on an existing post |
15
+ | `delete_post` | Delete a post and clean up its tag index |
16
+ | `upload_media` | Upload base64-encoded bytes to S3 and create a `Media` record |
17
+ | `get_schema` | Return the CMS content schema (Post / Page / Media field shapes) |
18
+
19
+ The server signs in to your Cognito User Pool with the credentials you provide (env vars), so each tool runs with the role of that user (`ampless-admin` or `ampless-editor`). Drafts and editing surface only to authenticated users; the resolver-side `status === 'published'` filter still keeps unpublished content invisible to public reads.
20
+
21
+ ## Install
22
+
23
+ The server is published as a Node CLI; you typically don't install it globally — point your MCP client at `npx -y @ampless/mcp-server@alpha` instead.
24
+
25
+ ## Configure
26
+
27
+ You need:
28
+
29
+ 1. **`amplify_outputs.json`** — generated by `npx ampx sandbox` or `npx ampx pipeline-deploy`. The path is passed via `--outputs`.
30
+ 2. **A Cognito user account** — email + password from your User Pool. The first user created via the admin UI is automatically in `ampless-admin`.
31
+ 3. **AWS credentials** — only required if you intend to use `upload_media`. The default credential chain (`AWS_PROFILE`, env vars, instance role) is used. Read-only tools work without AWS credentials.
32
+
33
+ ### Claude Desktop
34
+
35
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or the equivalent path on your platform:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "ampless": {
41
+ "command": "npx",
42
+ "args": [
43
+ "-y",
44
+ "@ampless/mcp-server",
45
+ "--outputs",
46
+ "/absolute/path/to/your-site/amplify_outputs.json"
47
+ ],
48
+ "env": {
49
+ "AMPLESS_MCP_EMAIL": "you@example.com",
50
+ "AMPLESS_MCP_PASSWORD": "your-password"
51
+ }
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ Restart Claude Desktop. The 7 tools should appear under `/mcp`.
58
+
59
+ ### Cursor
60
+
61
+ Edit `~/.cursor/mcp.json` (or use **Cursor Settings → MCP**). The config shape is the same as Claude Desktop:
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "ampless": {
67
+ "command": "npx",
68
+ "args": ["-y", "@ampless/mcp-server", "--outputs", "/path/to/amplify_outputs.json"],
69
+ "env": {
70
+ "AMPLESS_MCP_EMAIL": "you@example.com",
71
+ "AMPLESS_MCP_PASSWORD": "your-password"
72
+ }
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ ### Claude Code
79
+
80
+ Add a project-level MCP server:
81
+
82
+ ```bash
83
+ claude mcp add ampless \
84
+ --env AMPLESS_MCP_EMAIL=you@example.com \
85
+ --env AMPLESS_MCP_PASSWORD=your-password \
86
+ -- npx -y @ampless/mcp-server --outputs /path/to/amplify_outputs.json
87
+ ```
88
+
89
+ ## Usage examples
90
+
91
+ Once registered, prompt your AI agent:
92
+
93
+ - "List the latest 5 posts."
94
+ - "Show me the post with slug `welcome`."
95
+ - "Create a draft titled 'My second post' with format markdown and body 'Hello world.'"
96
+ - "Publish post `post-1234`."
97
+ - "Delete the post with slug `bad-draft`."
98
+
99
+ The agent will pick the right tool automatically.
100
+
101
+ ### `body` shape per `format`
102
+
103
+ - `format: 'markdown'` → body is a markdown source string
104
+ - `format: 'html'` → body is a raw HTML string (rendered verbatim — see editor trust model below)
105
+ - `format: 'tiptap'` → body is the tiptap document JSON, e.g.
106
+
107
+ ```json
108
+ {
109
+ "type": "doc",
110
+ "content": [
111
+ { "type": "paragraph", "content": [{ "type": "text", "text": "Hello" }] }
112
+ ]
113
+ }
114
+ ```
115
+
116
+ If you're letting the AI generate post bodies, asking for markdown is usually easiest.
117
+
118
+ ## Security notes
119
+
120
+ - **Editor trust model.** ampless treats `editor` and `admin` as a single trust class — both can store arbitrary HTML/JS in post bodies (see `docs/architecture/04-access-layer-mcp.md`). Whatever the MCP server can write, that user account could already write through the admin UI.
121
+ - **Credentials in the config file.** `AMPLESS_MCP_PASSWORD` lives in plaintext inside Claude Desktop / Cursor's config file. Treat that file like an SSH private key. v0.2 adds OS keychain integration.
122
+ - **AWS credentials.** Only `upload_media` requires them. Use a dedicated IAM user / role with write access only to your site's S3 bucket.
123
+
124
+ ## CLI flags
125
+
126
+ ```
127
+ ampless-mcp [options]
128
+
129
+ --outputs <path> Path to amplify_outputs.json (also AMPLESS_MCP_OUTPUTS)
130
+ --site-id <id> Default siteId for queries (also AMPLESS_MCP_SITE_ID; default "default")
131
+ ```
132
+
133
+ Required env:
134
+
135
+ ```
136
+ AMPLESS_MCP_EMAIL Cognito user email
137
+ AMPLESS_MCP_PASSWORD Cognito user password
138
+ ```
139
+
140
+ ## Troubleshooting
141
+
142
+ - **`NotAuthorizedException: Incorrect username or password.`** — Wrong creds, or the user hasn't confirmed their email. Sign in via the web `/login` once first.
143
+ - **`InvalidPasswordException`** — The user is in `FORCE_CHANGE_PASSWORD` state. Sign in via the web UI to set a permanent password.
144
+ - **`AppSync 401`** — id token may have been rejected; the server auto-refreshes on the next call. Repeated 401s usually mean the User Pool was redeployed and your creds need updating.
145
+ - **`upload_media` fails with no AWS credentials** — set `AWS_PROFILE` or `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` in the MCP server's environment.
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../../node_modules/.pnpm/@smithy+core@3.23.17/node_modules/@smithy/core/dist-es/util-identity-and-auth/httpAuthSchemes/noAuth.js
4
+ var NoAuthSigner = class {
5
+ async sign(httpRequest, identity, signingProperties) {
6
+ return httpRequest;
7
+ }
8
+ };
9
+
10
+ // ../../node_modules/.pnpm/@aws-sdk+nested-clients@3.997.5/node_modules/@aws-sdk/nested-clients/package.json
11
+ var package_default = {
12
+ name: "@aws-sdk/nested-clients",
13
+ version: "3.997.5",
14
+ description: "Nested clients for AWS SDK packages.",
15
+ main: "./dist-cjs/index.js",
16
+ module: "./dist-es/index.js",
17
+ types: "./dist-types/index.d.ts",
18
+ scripts: {
19
+ build: "yarn lint && concurrently 'yarn:build:types' 'yarn:build:es' && yarn build:cjs",
20
+ "build:cjs": "node ../../scripts/compilation/inline nested-clients",
21
+ "build:es": "tsc -p tsconfig.es.json",
22
+ "build:include:deps": 'yarn g:turbo run build -F="$npm_package_name"',
23
+ "build:types": "tsc -p tsconfig.types.json",
24
+ "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
25
+ clean: "premove dist-cjs dist-es dist-types tsconfig.cjs.tsbuildinfo tsconfig.es.tsbuildinfo tsconfig.types.tsbuildinfo",
26
+ lint: "node ../../scripts/validation/submodules-linter.js --pkg nested-clients",
27
+ test: "yarn g:vitest run",
28
+ "test:watch": "yarn g:vitest watch"
29
+ },
30
+ engines: {
31
+ node: ">=20.0.0"
32
+ },
33
+ sideEffects: false,
34
+ author: {
35
+ name: "AWS SDK for JavaScript Team",
36
+ url: "https://aws.amazon.com/javascript/"
37
+ },
38
+ license: "Apache-2.0",
39
+ dependencies: {
40
+ "@aws-crypto/sha256-browser": "5.2.0",
41
+ "@aws-crypto/sha256-js": "5.2.0",
42
+ "@aws-sdk/core": "^3.974.7",
43
+ "@aws-sdk/middleware-host-header": "^3.972.10",
44
+ "@aws-sdk/middleware-logger": "^3.972.10",
45
+ "@aws-sdk/middleware-recursion-detection": "^3.972.11",
46
+ "@aws-sdk/middleware-user-agent": "^3.972.37",
47
+ "@aws-sdk/region-config-resolver": "^3.972.13",
48
+ "@aws-sdk/signature-v4-multi-region": "^3.996.24",
49
+ "@aws-sdk/types": "^3.973.8",
50
+ "@aws-sdk/util-endpoints": "^3.996.8",
51
+ "@aws-sdk/util-user-agent-browser": "^3.972.10",
52
+ "@aws-sdk/util-user-agent-node": "^3.973.23",
53
+ "@smithy/config-resolver": "^4.4.17",
54
+ "@smithy/core": "^3.23.17",
55
+ "@smithy/fetch-http-handler": "^5.3.17",
56
+ "@smithy/hash-node": "^4.2.14",
57
+ "@smithy/invalid-dependency": "^4.2.14",
58
+ "@smithy/middleware-content-length": "^4.2.14",
59
+ "@smithy/middleware-endpoint": "^4.4.32",
60
+ "@smithy/middleware-retry": "^4.5.7",
61
+ "@smithy/middleware-serde": "^4.2.20",
62
+ "@smithy/middleware-stack": "^4.2.14",
63
+ "@smithy/node-config-provider": "^4.3.14",
64
+ "@smithy/node-http-handler": "^4.6.1",
65
+ "@smithy/protocol-http": "^5.3.14",
66
+ "@smithy/smithy-client": "^4.12.13",
67
+ "@smithy/types": "^4.14.1",
68
+ "@smithy/url-parser": "^4.2.14",
69
+ "@smithy/util-base64": "^4.3.2",
70
+ "@smithy/util-body-length-browser": "^4.2.2",
71
+ "@smithy/util-body-length-node": "^4.2.3",
72
+ "@smithy/util-defaults-mode-browser": "^4.3.49",
73
+ "@smithy/util-defaults-mode-node": "^4.2.54",
74
+ "@smithy/util-endpoints": "^3.4.2",
75
+ "@smithy/util-middleware": "^4.2.14",
76
+ "@smithy/util-retry": "^4.3.6",
77
+ "@smithy/util-utf8": "^4.2.2",
78
+ tslib: "^2.6.2"
79
+ },
80
+ devDependencies: {
81
+ concurrently: "7.0.0",
82
+ "downlevel-dts": "0.10.1",
83
+ premove: "4.0.0",
84
+ typescript: "~5.8.3"
85
+ },
86
+ typesVersions: {
87
+ "<4.5": {
88
+ "dist-types/*": [
89
+ "dist-types/ts3.4/*"
90
+ ]
91
+ }
92
+ },
93
+ files: [
94
+ "./cognito-identity.d.ts",
95
+ "./cognito-identity.js",
96
+ "./signin.d.ts",
97
+ "./signin.js",
98
+ "./sso-oidc.d.ts",
99
+ "./sso-oidc.js",
100
+ "./sso.d.ts",
101
+ "./sso.js",
102
+ "./sts.d.ts",
103
+ "./sts.js",
104
+ "dist-*/**"
105
+ ],
106
+ browser: {
107
+ "./dist-es/submodules/cognito-identity/runtimeConfig": "./dist-es/submodules/cognito-identity/runtimeConfig.browser",
108
+ "./dist-es/submodules/signin/runtimeConfig": "./dist-es/submodules/signin/runtimeConfig.browser",
109
+ "./dist-es/submodules/sso-oidc/runtimeConfig": "./dist-es/submodules/sso-oidc/runtimeConfig.browser",
110
+ "./dist-es/submodules/sso/runtimeConfig": "./dist-es/submodules/sso/runtimeConfig.browser",
111
+ "./dist-es/submodules/sts/runtimeConfig": "./dist-es/submodules/sts/runtimeConfig.browser"
112
+ },
113
+ "react-native": {},
114
+ homepage: "https://github.com/aws/aws-sdk-js-v3/tree/main/packages/nested-clients",
115
+ repository: {
116
+ type: "git",
117
+ url: "https://github.com/aws/aws-sdk-js-v3.git",
118
+ directory: "packages/nested-clients"
119
+ },
120
+ exports: {
121
+ "./package.json": "./package.json",
122
+ "./sso-oidc": {
123
+ types: "./dist-types/submodules/sso-oidc/index.d.ts",
124
+ module: "./dist-es/submodules/sso-oidc/index.js",
125
+ node: "./dist-cjs/submodules/sso-oidc/index.js",
126
+ import: "./dist-es/submodules/sso-oidc/index.js",
127
+ require: "./dist-cjs/submodules/sso-oidc/index.js"
128
+ },
129
+ "./sts": {
130
+ types: "./dist-types/submodules/sts/index.d.ts",
131
+ module: "./dist-es/submodules/sts/index.js",
132
+ node: "./dist-cjs/submodules/sts/index.js",
133
+ import: "./dist-es/submodules/sts/index.js",
134
+ require: "./dist-cjs/submodules/sts/index.js"
135
+ },
136
+ "./signin": {
137
+ types: "./dist-types/submodules/signin/index.d.ts",
138
+ module: "./dist-es/submodules/signin/index.js",
139
+ node: "./dist-cjs/submodules/signin/index.js",
140
+ import: "./dist-es/submodules/signin/index.js",
141
+ require: "./dist-cjs/submodules/signin/index.js"
142
+ },
143
+ "./cognito-identity": {
144
+ types: "./dist-types/submodules/cognito-identity/index.d.ts",
145
+ module: "./dist-es/submodules/cognito-identity/index.js",
146
+ node: "./dist-cjs/submodules/cognito-identity/index.js",
147
+ import: "./dist-es/submodules/cognito-identity/index.js",
148
+ require: "./dist-cjs/submodules/cognito-identity/index.js"
149
+ },
150
+ "./sso": {
151
+ types: "./dist-types/submodules/sso/index.d.ts",
152
+ module: "./dist-es/submodules/sso/index.js",
153
+ node: "./dist-cjs/submodules/sso/index.js",
154
+ import: "./dist-es/submodules/sso/index.js",
155
+ require: "./dist-cjs/submodules/sso/index.js"
156
+ }
157
+ }
158
+ };
159
+
160
+ export {
161
+ NoAuthSigner,
162
+ package_default
163
+ };
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../../node_modules/.pnpm/@aws-sdk+core@3.974.7/node_modules/@aws-sdk/core/dist-es/submodules/client/setCredentialFeature.js
4
+ function setCredentialFeature(credentials, feature, value) {
5
+ if (!credentials.$source) {
6
+ credentials.$source = {};
7
+ }
8
+ credentials.$source[feature] = value;
9
+ return credentials;
10
+ }
11
+
12
+ export {
13
+ setCredentialFeature
14
+ };
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../../node_modules/.pnpm/@smithy+protocol-http@5.3.14/node_modules/@smithy/protocol-http/dist-es/httpRequest.js
4
+ var HttpRequest = class _HttpRequest {
5
+ method;
6
+ protocol;
7
+ hostname;
8
+ port;
9
+ path;
10
+ query;
11
+ headers;
12
+ username;
13
+ password;
14
+ fragment;
15
+ body;
16
+ constructor(options) {
17
+ this.method = options.method || "GET";
18
+ this.hostname = options.hostname || "localhost";
19
+ this.port = options.port;
20
+ this.query = options.query || {};
21
+ this.headers = options.headers || {};
22
+ this.body = options.body;
23
+ this.protocol = options.protocol ? options.protocol.slice(-1) !== ":" ? `${options.protocol}:` : options.protocol : "https:";
24
+ this.path = options.path ? options.path.charAt(0) !== "/" ? `/${options.path}` : options.path : "/";
25
+ this.username = options.username;
26
+ this.password = options.password;
27
+ this.fragment = options.fragment;
28
+ }
29
+ static clone(request) {
30
+ const cloned = new _HttpRequest({
31
+ ...request,
32
+ headers: { ...request.headers }
33
+ });
34
+ if (cloned.query) {
35
+ cloned.query = cloneQuery(cloned.query);
36
+ }
37
+ return cloned;
38
+ }
39
+ static isInstance(request) {
40
+ if (!request) {
41
+ return false;
42
+ }
43
+ const req = request;
44
+ return "method" in req && "protocol" in req && "hostname" in req && "path" in req && typeof req["query"] === "object" && typeof req["headers"] === "object";
45
+ }
46
+ clone() {
47
+ return _HttpRequest.clone(this);
48
+ }
49
+ };
50
+ function cloneQuery(query) {
51
+ return Object.keys(query).reduce((carry, paramName) => {
52
+ const param = query[paramName];
53
+ return {
54
+ ...carry,
55
+ [paramName]: Array.isArray(param) ? [...param] : param
56
+ };
57
+ }, {});
58
+ }
59
+
60
+ export {
61
+ HttpRequest
62
+ };
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../../node_modules/.pnpm/@smithy+smithy-client@4.12.13/node_modules/@smithy/smithy-client/dist-es/create-aggregated-client.js
4
+ var createAggregatedClient = (commands, Client, options) => {
5
+ for (const [command, CommandCtor] of Object.entries(commands)) {
6
+ const methodImpl = async function(args, optionsOrCb, cb) {
7
+ const command2 = new CommandCtor(args);
8
+ if (typeof optionsOrCb === "function") {
9
+ this.send(command2, optionsOrCb);
10
+ } else if (typeof cb === "function") {
11
+ if (typeof optionsOrCb !== "object")
12
+ throw new Error(`Expected http options but got ${typeof optionsOrCb}`);
13
+ this.send(command2, optionsOrCb || {}, cb);
14
+ } else {
15
+ return this.send(command2, optionsOrCb);
16
+ }
17
+ };
18
+ const methodName = (command[0].toLowerCase() + command.slice(1)).replace(/Command$/, "");
19
+ Client.prototype[methodName] = methodImpl;
20
+ }
21
+ const { paginators = {}, waiters = {} } = options ?? {};
22
+ for (const [paginatorName, paginatorFn] of Object.entries(paginators)) {
23
+ if (Client.prototype[paginatorName] === void 0) {
24
+ Client.prototype[paginatorName] = function(commandInput = {}, paginationConfiguration, ...rest) {
25
+ return paginatorFn({
26
+ ...paginationConfiguration,
27
+ client: this
28
+ }, commandInput, ...rest);
29
+ };
30
+ }
31
+ }
32
+ for (const [waiterName, waiterFn] of Object.entries(waiters)) {
33
+ if (Client.prototype[waiterName] === void 0) {
34
+ Client.prototype[waiterName] = async function(commandInput = {}, waiterConfiguration, ...rest) {
35
+ let config = waiterConfiguration;
36
+ if (typeof waiterConfiguration === "number") {
37
+ config = {
38
+ maxWaitTime: waiterConfiguration
39
+ };
40
+ }
41
+ return waiterFn({
42
+ ...config,
43
+ client: this
44
+ }, commandInput, ...rest);
45
+ };
46
+ }
47
+ }
48
+ };
49
+
50
+ export {
51
+ createAggregatedClient
52
+ };
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../../node_modules/.pnpm/@smithy+is-array-buffer@4.2.2/node_modules/@smithy/is-array-buffer/dist-es/index.js
4
+ var isArrayBuffer = (arg) => typeof ArrayBuffer === "function" && arg instanceof ArrayBuffer || Object.prototype.toString.call(arg) === "[object ArrayBuffer]";
5
+
6
+ // ../../node_modules/.pnpm/@smithy+util-buffer-from@4.2.2/node_modules/@smithy/util-buffer-from/dist-es/index.js
7
+ import { Buffer } from "buffer";
8
+ var fromArrayBuffer = (input, offset = 0, length = input.byteLength - offset) => {
9
+ if (!isArrayBuffer(input)) {
10
+ throw new TypeError(`The "input" argument must be ArrayBuffer. Received type ${typeof input} (${input})`);
11
+ }
12
+ return Buffer.from(input, offset, length);
13
+ };
14
+ var fromString = (input, encoding) => {
15
+ if (typeof input !== "string") {
16
+ throw new TypeError(`The "input" argument must be of type string. Received type ${typeof input} (${input})`);
17
+ }
18
+ return encoding ? Buffer.from(input, encoding) : Buffer.from(input);
19
+ };
20
+
21
+ // ../../node_modules/.pnpm/@smithy+util-utf8@4.2.2/node_modules/@smithy/util-utf8/dist-es/fromUtf8.js
22
+ var fromUtf8 = (input) => {
23
+ const buf = fromString(input, "utf8");
24
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength / Uint8Array.BYTES_PER_ELEMENT);
25
+ };
26
+
27
+ // ../../node_modules/.pnpm/@smithy+util-utf8@4.2.2/node_modules/@smithy/util-utf8/dist-es/toUtf8.js
28
+ var toUtf8 = (input) => {
29
+ if (typeof input === "string") {
30
+ return input;
31
+ }
32
+ if (typeof input !== "object" || typeof input.byteOffset !== "number" || typeof input.byteLength !== "number") {
33
+ throw new Error("@smithy/util-utf8: toUtf8 encoder function only accepts string | Uint8Array.");
34
+ }
35
+ return fromArrayBuffer(input.buffer, input.byteOffset, input.byteLength).toString("utf8");
36
+ };
37
+
38
+ export {
39
+ isArrayBuffer,
40
+ fromArrayBuffer,
41
+ fromString,
42
+ fromUtf8,
43
+ toUtf8
44
+ };