@jinshuju/mcp 0.2.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.
package/dist/result.d.ts CHANGED
@@ -5,4 +5,6 @@
5
5
  import type { CallToolResult } from '@modelcontextprotocol/server';
6
6
  import type { ApiResponse } from './api.js';
7
7
  import type { ToolSpec } from './types.js';
8
+ /** The 403 hint; it names the scope itself, so analytics.ts leaves it out when looking for a scope failure in the API's own words. */
9
+ export declare const FORBIDDEN_HINT = "No permission, a quota is used up, or the OAuth scope is insufficient.";
8
10
  export declare function toToolResult(tool: ToolSpec, res: ApiResponse, maxResultChars: number): CallToolResult;
package/dist/result.js CHANGED
@@ -21,6 +21,8 @@ function errorLines(res) {
21
21
  lines.push(res.text);
22
22
  return lines;
23
23
  }
24
+ /** The 403 hint; it names the scope itself, so analytics.ts leaves it out when looking for a scope failure in the API's own words. */
25
+ export const FORBIDDEN_HINT = 'No permission, a quota is used up, or the OAuth scope is insufficient.';
24
26
  function hint(status, res) {
25
27
  switch (status) {
26
28
  case 400:
@@ -30,7 +32,7 @@ function hint(status, res) {
30
32
  case 402:
31
33
  return 'The current plan does not include API v1.';
32
34
  case 403:
33
- return 'No permission, a quota is used up, or the OAuth scope is insufficient.';
35
+ return FORBIDDEN_HINT;
34
36
  case 404:
35
37
  return 'The resource does not exist or this credential cannot access it (check the token / serial number).';
36
38
  case 415:
package/dist/server.js CHANGED
@@ -12,7 +12,7 @@ export const INSTRUCTIONS = [
12
12
  'MCP server for Jinshuju (金数据) forms, tables and entries. Each tool maps to the open API v1 and is generated from its openapi.yaml.',
13
13
  "Usual path: list_forms finds the form token → get_form shows the fields' api_code → list_entries / count_entries / aggregate_entries read entries;",
14
14
  'create_entry / patch_entries write them, keyed by api_code.',
15
- 'To create a form: get_schema("FieldInput") lists the field types, get_schema("<Type>Input") shows a type\'s properties, then call create_form.',
15
+ 'To create a form: get_schema("FieldInput") maps each field type to its schema in x-variants (TextField → TextFieldInput, SingleSelect → SingleSelectInput), get_schema on that name shows the type\'s properties, then call create_form.',
16
16
  'Wherever an input says `Shape: schema "X"`, look X up with get_schema; do not guess.',
17
17
  "An error gives the HTTP status and the server's error_description / errors; fix the call as they say and retry. On 429, wait as Retry-After says.",
18
18
  'A personal token sees only the forms its user created or that are shared with them; an account token sees every form in the account.'
package/dist/tools.js CHANGED
@@ -47,6 +47,40 @@ export function registerGeneratedTools(server, runtime) {
47
47
  export function schemaNames() {
48
48
  return Object.keys(SCHEMAS);
49
49
  }
50
+ /** Lower-case words of a schema or tool name, without Input; edit reads as update, since tools say edit where schemas say Update. */
51
+ function nameWords(name) {
52
+ const words = name.match(/[A-Z]+(?![a-z])|[A-Z]?[a-z]+|\d+/g) ?? [];
53
+ return new Set(words
54
+ .map((word) => word.toLowerCase())
55
+ .filter((word) => word !== 'input')
56
+ .map((word) => (word === 'edit' ? 'update' : word)));
57
+ }
58
+ /** Schema names containing the name come first, then those sharing the most words with it. */
59
+ function similarSchemaNames(name) {
60
+ const lower = name.toLowerCase();
61
+ const words = nameWords(name);
62
+ const score = (candidate) => {
63
+ if (candidate.toLowerCase().includes(lower))
64
+ return 2;
65
+ const other = nameWords(candidate);
66
+ const shared = [...words].filter((word) => other.has(word)).length;
67
+ return shared / new Set([...words, ...other]).size;
68
+ };
69
+ return schemaNames()
70
+ .map((candidate) => ({ candidate, score: score(candidate) }))
71
+ .filter((scored) => scored.score > 0)
72
+ .sort((a, b) => b.score - a.score)
73
+ .slice(0, 5)
74
+ .map((scored) => scored.candidate);
75
+ }
76
+ /** get_schema's answer to an unknown name: a guessed tool input (EditFormInput) is pointed back at the tool, anything else gets similar names. */
77
+ function unknownSchemaMessage(name) {
78
+ const tool = TOOLS.find((t) => `${t.name.replaceAll('_', '')}input` === name.toLowerCase());
79
+ if (tool)
80
+ return `No schema named ${name}. ${tool.name} takes the arguments listed in its own inputSchema; there is no schema to look up for them.`;
81
+ const candidates = similarSchemaNames(name);
82
+ return `No schema named ${name}.${candidates.length ? ` Similar names: ${candidates.join(', ')}` : ''}`;
83
+ }
50
84
  export function registerSchemaTools(server, pointerSchemas) {
51
85
  server.registerTool('get_schema', {
52
86
  title: 'Show a schema',
@@ -56,7 +90,10 @@ export function registerSchemaTools(server, pointerSchemas) {
56
90
  inputSchema: fromJsonSchema({
57
91
  type: 'object',
58
92
  properties: {
59
- name: { type: 'string', description: 'Schema name, e.g. FieldInput, TextFieldInput, FormSettingWriteInput' }
93
+ name: {
94
+ type: 'string',
95
+ description: 'Schema name, e.g. FieldInput, TextFieldInput, SingleSelectInput, FormSettingWriteInput'
96
+ }
60
97
  },
61
98
  required: ['name'],
62
99
  additionalProperties: false
@@ -64,20 +101,8 @@ export function registerSchemaTools(server, pointerSchemas) {
64
101
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
65
102
  }, async ({ name }) => {
66
103
  const schema = SCHEMAS[name];
67
- if (!schema) {
68
- const candidates = schemaNames()
69
- .filter((n) => n.toLowerCase().includes(name.toLowerCase()))
70
- .slice(0, 10);
71
- return {
72
- isError: true,
73
- content: [
74
- {
75
- type: 'text',
76
- text: `No schema named ${name}.${candidates.length ? ` Similar names: ${candidates.join(', ')}` : ''}`
77
- }
78
- ]
79
- };
80
- }
104
+ if (!schema)
105
+ return { isError: true, content: [{ type: 'text', text: unknownSchemaMessage(name) }] };
81
106
  return { content: [{ type: 'text', text: JSON.stringify(schema) }] };
82
107
  });
83
108
  }
package/dist/types.d.ts CHANGED
@@ -9,6 +9,8 @@ export interface ParamSpec {
9
9
  required: boolean;
10
10
  /** A query array is serialized as name[]=a&name[]=b (the server reads it as an array) */
11
11
  array?: boolean;
12
+ /** Declared with `content: application/json`: sent as one query value holding JSON text (a string is passed through as is) */
13
+ json?: boolean;
12
14
  }
13
15
  export interface BodySpec {
14
16
  contentType: 'application/json' | 'multipart/form-data';
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export declare const VERSION = "0.2.0";
1
+ export declare const VERSION = "0.4.0";
2
2
  export declare const SERVER_NAME = "jinshuju";
3
3
  export declare const SERVER_TITLE = "Jinshuju MCP Server";
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
- // release-please keeps this in sync through extra-files; the runtime does not read package.json because Workers has no file system.
2
- export const VERSION = '0.2.0'; // x-release-please-version
1
+ // scripts/release.mjs keeps this in step with package.json; the runtime does not read package.json because Workers has no file system.
2
+ export const VERSION = '0.4.0'; // x-release-version
3
3
  export const SERVER_NAME = 'jinshuju';
4
4
  export const SERVER_TITLE = 'Jinshuju MCP Server';
5
5
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jinshuju/mcp",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "MCP server for the Jinshuju (金数据) Open API v1, generated from openapi.yaml",
5
5
  "homepage": "https://github.com/jinshuju/mcp#readme",
6
6
  "bugs": {
@@ -66,7 +66,7 @@
66
66
  "oxfmt": "^0.72.0",
67
67
  "oxlint": "^1.87.0",
68
68
  "tsx": "^4.23.0",
69
- "typescript": "^5.9.3",
69
+ "typescript": "^7.0.2",
70
70
  "wrangler": "^4.149.0",
71
71
  "yaml": "^2.9.1"
72
72
  },