create-solvapay 0.8.1 → 0.8.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-solvapay",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Scaffold SolvaPay-monetized projects — MCP servers on Cloudflare Workers and more.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -625,7 +625,8 @@ function deriveServerNameFromWorkerName(workerName) {
625
625
  return cleaned || 'solvapay-mcp-server'
626
626
  }
627
627
 
628
- function renderToolFile({ operation, schemes, tier, auth, serverBaseUrl, freeLimit }) {
628
+ function renderToolFile({ operation: specOperation, schemes, tier, auth, serverBaseUrl, freeLimit }) {
629
+ const operation = withoutReservedHeaderParams(specOperation, auth)
629
630
  const fnName = `register${toPascalIdentifier(operation.operationId)}`
630
631
  const urlTemplate = renderUrlTemplate(serverBaseUrl, operation)
631
632
  const headerLines = renderHeaderLines(auth, schemes, operation)
@@ -765,7 +766,7 @@ function shortenDescription(text) {
765
766
  function renderUrlTemplate(baseUrl, operation) {
766
767
  let path = operation.path
767
768
  for (const param of operation.parameters.filter(p => p.in === 'path')) {
768
- path = path.replace(`{${param.name}}`, `\${input.${jsAccessor(param.name)}}`)
769
+ path = path.replace(`{${param.name}}`, `\${${inputAccess(param.name)}}`)
769
770
  }
770
771
  return `\`${joinUrl(baseUrl, path)}\``
771
772
  }
@@ -783,7 +784,7 @@ function renderFormAssign(operation) {
783
784
  const ctor = operation.formEncoding === 'multipart' ? 'FormData' : 'URLSearchParams'
784
785
  const lines = [` const form = new ${ctor}()`]
785
786
  for (const field of fields) {
786
- const access = `input.${jsAccessor(field.name)}`
787
+ const access = inputAccess(field.name)
787
788
  lines.push(` if (${access} !== undefined) form.set('${field.name}', String(${access}))`)
788
789
  }
789
790
  return lines.join('\n')
@@ -795,7 +796,7 @@ function renderFetchInit(operation, headerLines) {
795
796
  const queryAssign = operation.parameters
796
797
  .filter(p => p.in === 'query')
797
798
  .map(p => {
798
- const access = `input.${jsAccessor(p.name)}`
799
+ const access = inputAccess(p.name)
799
800
  return ` if (${access} !== undefined) url.searchParams.set('${p.name}', String(${access}))`
800
801
  })
801
802
  .concat(renderFormAssign(operation) || [])
@@ -821,6 +822,22 @@ function renderFetchInit(operation, headerLines) {
821
822
  return { method, queryAssign, headersBlock, bodyBlock, methodBlock, usesStringifyLossless }
822
823
  }
823
824
 
825
+ // OpenAPI ignores `in: header` params named Accept, Content-Type or
826
+ // Authorization, and a header param that names the upstream credential
827
+ // header would let the LLM supply (or overwrite) the secret. Drop both
828
+ // from the tool input.
829
+ function withoutReservedHeaderParams(operation, auth) {
830
+ const reserved = new Set(['accept', 'content-type', 'authorization'])
831
+ if (auth.kind === 'apiKey' && auth.name) reserved.add(auth.name.toLowerCase())
832
+ if (auth.kind === 'apiKey-multi') {
833
+ for (const header of auth.headers ?? []) reserved.add(String(header.name).toLowerCase())
834
+ }
835
+ const parameters = operation.parameters.filter(
836
+ p => p.in !== 'header' || !reserved.has(String(p.name).toLowerCase()),
837
+ )
838
+ return parameters.length === operation.parameters.length ? operation : { ...operation, parameters }
839
+ }
840
+
824
841
  function renderHeaderLines(auth, schemes, operation) {
825
842
  // Pick the first supported scheme this operation requires; fall back
826
843
  // to the top-level auth kind when the spec has no operation-level
@@ -838,7 +855,14 @@ function renderHeaderLines(auth, schemes, operation) {
838
855
  // handler body (see renderPayableBody / renderFreeCappedBody /
839
856
  // renderFreeBody) via
840
857
  // `const token = await getAccessToken(env)`.
841
- const headerEntries = []
858
+ // `in: header` params go first so the upstream credential (added below)
859
+ // always wins over a caller-supplied value.
860
+ const headerEntries = operation.parameters
861
+ .filter(p => p.in === 'header')
862
+ .map(p => {
863
+ const access = inputAccess(p.name)
864
+ return `...(${access} !== undefined ? { ${JSON.stringify(p.name.toLowerCase())}: String(${access}) } : {})`
865
+ })
842
866
  if (auth.kind === 'bearer') {
843
867
  headerEntries.push("authorization: `Bearer ${env.UPSTREAM_API_KEY}`")
844
868
  } else if (auth.kind === 'apiKey') {
@@ -1126,6 +1150,12 @@ function jsAccessor(name) {
1126
1150
  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : `[${JSON.stringify(name)}]`
1127
1151
  }
1128
1152
 
1153
+ // `input.foo` for identifier-safe names, `input["page-size"]` otherwise.
1154
+ function inputAccess(name) {
1155
+ const accessor = jsAccessor(name)
1156
+ return accessor.startsWith('[') ? `input${accessor}` : `input.${accessor}`
1157
+ }
1158
+
1129
1159
  function capitalize(s) {
1130
1160
  return s.charAt(0).toUpperCase() + s.slice(1)
1131
1161
  }