@unravel-tech/thing 0.3.0 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +22 -4
  2. package/package.json +1 -1
  3. package/src/index.js +11 -3
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # @unravel-tech/thing
2
2
 
3
- CLI for [thing](https://thing.unravel.tech): push, version, and share artifacts HTML pages, Markdown docs, standalone images and PDFs from coding agents.
3
+ CLI for [thing](https://usething.ai): push, version, and share artifacts (HTML pages, Markdown docs, standalone images and PDFs) from coding agents.
4
4
 
5
5
  ```sh
6
6
  npm i -g @unravel-tech/thing
7
- thing login # defaults to https://thing.unravel.tech
7
+ thing login # defaults to https://usething.ai
8
8
  thing push report.html --json
9
9
  thing push notes.md --json # Markdown gets a styled reader view
10
10
  thing push chart.png --json # images and PDFs too
@@ -38,10 +38,28 @@ the overrides set the server picks your default (e.g. the Unravel org for Unrave
38
38
 
39
39
  ## MCP
40
40
 
41
- Any MCP client can push artifacts through your CLI login — register the server as:
41
+ Claude Code, Cursor, Codex, and anything else that speaks Model Context
42
+ Protocol can publish through thing. No terminal login needed: create a token at
43
+ [usething.ai](https://usething.ai) under Settings, then Tokens, and paste this
44
+ into your client's MCP config.
42
45
 
43
46
  ```json
44
- { "command": "thing", "args": ["mcp"] }
47
+ {
48
+ "mcpServers": {
49
+ "thing": {
50
+ "command": "npx",
51
+ "args": ["-y", "@unravel-tech/thing", "mcp"],
52
+ "env": { "THING_TOKEN": "paste-your-token-here" }
53
+ }
54
+ }
55
+ }
45
56
  ```
46
57
 
58
+ The server exposes three tools: `push_artifact` to publish a file or inline
59
+ content and get back a link, `list_artifacts` to see what you have, and
60
+ `whoami` to check which account and team you are pushing to.
61
+
62
+ If you already ran `thing login`, the stored credential is used and
63
+ `THING_TOKEN` can be left out.
64
+
47
65
  Requires Node >= 18 or Bun. No runtime dependencies.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.0",
6
+ "version": "0.4.0",
7
7
  "description": "CLI for thing: push, version, and share artifacts (HTML, Markdown, images, PDFs) from coding agents.",
8
8
  "license": "MIT",
9
9
  "repository": {
package/src/index.js CHANGED
@@ -5,7 +5,8 @@ import { homedir } from "node:os";
5
5
  import { spawn } from "node:child_process";
6
6
  import { pathToFileURL } from "node:url";
7
7
 
8
- const DEFAULT_SERVER = process.env.THING_SERVER || "https://thing.unravel.tech";
8
+ const DEFAULT_SERVER = process.env.THING_SERVER || "https://usething.ai";
9
+ let clientName = "thing-cli/0.3.0";
9
10
  const VISIBILITIES = new Set(["team", "public"]);
10
11
 
11
12
  class CliError extends Error {
@@ -36,6 +37,7 @@ function projectConfigPath(cwd) {
36
37
 
37
38
  function loadState(cwd, env = process.env) {
38
39
  return {
40
+ env,
39
41
  globalPath: configPath(env),
40
42
  global: readJson(configPath(env)),
41
43
  projectPath: projectConfigPath(cwd),
@@ -87,9 +89,13 @@ function stripSlash(server) {
87
89
  }
88
90
 
89
91
  function context(state, flags = {}) {
92
+ const env = state.env ?? process.env;
90
93
  return {
91
- server: stripSlash(flags.server || state.project.server || state.global.server || DEFAULT_SERVER),
92
- token: flags.token || state.global.token || null,
94
+ // THING_SERVER and THING_TOKEN let an MCP client run this with no prior
95
+ // `thing login`: the whole config is a paste, which is the difference
96
+ // between usable and not for anyone who does not live in a terminal.
97
+ server: stripSlash(flags.server || env.THING_SERVER || state.project.server || state.global.server || DEFAULT_SERVER),
98
+ token: flags.token || env.THING_TOKEN || state.global.token || null,
93
99
  team: flags.team || state.project.team || state.global.activeTeam || null,
94
100
  project: flags.project || state.project.project || state.global.activeProject || null
95
101
  };
@@ -129,6 +135,7 @@ function formatText(value) {
129
135
  async function api(ctx, path, options = {}) {
130
136
  const headers = {
131
137
  Accept: "application/json",
138
+ "X-Thing-Client": clientName,
132
139
  ...(options.body ? { "Content-Type": "application/json" } : {}),
133
140
  ...(ctx.token ? { Authorization: `Bearer ${ctx.token}` } : {}),
134
141
  ...(options.headers || {})
@@ -481,6 +488,7 @@ async function mcpTool(state, parsed, name, args) {
481
488
  }
482
489
 
483
490
  async function mcp(parsed, state, io) {
491
+ clientName = "thing-mcp/0.3.0";
484
492
  const respond = (id, body) => io.stdout.write(`${JSON.stringify({ jsonrpc: "2.0", id, ...body })}\n`);
485
493
  const handle = async (req) => {
486
494
  if (req.method === "initialize") {